﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Drawing.Imaging;
using System.Reflection;

namespace MedicWave.Visualization3d
{
    delegate void UpdatePlotTypeDelegate(PlotType newType);
    delegate void UpdatePlotSettingsDelegate(InternalPlotSettings newSettings);
    delegate void SetDataDelegate(InternalPlotData newData);

    delegate CustomVertex.PositionColored[] PlotBuilderDelegate(out int[] aVertices);

    struct PositionNormalTextured
    {
        public Vector3 Position;
        public Vector3 Normal;
        //public Vector3 Tangent;
        //public Vector3 Binormal;
        public Vector2 Coords;
        public static readonly VertexElement[] Declarator = new VertexElement[]
        {
            new VertexElement( 0, 0, DeclarationType.Float3,
            DeclarationMethod.Default, DeclarationUsage.Position, 0 ),
            new VertexElement( 0, 12, DeclarationType.Float3,
            DeclarationMethod.Default, DeclarationUsage.Normal, 0 ),
            //new VertexElement( 0, 24, DeclarationType.Float3,
            //DeclarationMethod.Default, DeclarationUsage.Tangent, 0 ),
            //new VertexElement( 0, 36, DeclarationType.Float3,
            //DeclarationMethod.Default, DeclarationUsage.BiNormal, 0 ),
            new VertexElement( 0, 24, DeclarationType.Float2,
            DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0 ),
            VertexElement.VertexDeclarationEnd
        };

    }

    class Plot3DControl : DXControl
    {
        //public enum PlotTypes { Points, Lines, Surface, None };
        public enum AxisTypes { Flat, Solid };
        //public enum ProjectionType { Ortho, Perspective };


        private Microsoft.DirectX.Direct3D.Font m_debugFont = null;
        private Microsoft.DirectX.Direct3D.Font m_LoadingFont = null;
        private Microsoft.DirectX.Direct3D.Font m_PlotTitleFont = null;
        private Sprite m_spriteObject = null;
        private int m_iFrameNumber = 0;


        //////////////////////////////////////////////////////////////////////////
        //camera data
        private float m_fMouseRotateSensitivity = 100f;
        private float m_fMouseMoveSensitivity = 300f;
        private Point m_previousMousePosition;
        private float m_fCameraRotationY = 0;
        private float m_fCameraRotationX = 0;
        private float m_fInitialFoV = (float)Math.PI / 4;
        private float m_fCurrentFoV = (float)Math.PI / 4;
        private float m_fMaxCameraDistance = 200;
        private float m_fCurrentCameraDistance = 200;


        private float m_fMinCameraDistance = 100f;
        private Vector3 m_vInitialCameraPos = new Vector3(-50.0f, 0f, 180.0f);
        private Vector3 m_vCameraPos = new Vector3(-50f, 0f, 200.0f);
        private Vector3 m_vInitialLookAt = new Vector3(-20, 0, 0);
        private Vector3 m_vLookAt = new Vector3(-50, 0, 0);
        private Vector3 m_vPlotArea = new Vector3(100, 50, 100);
        private Vector3 m_vPlotPosition = new Vector3(0, 0, 0);

        private Matrix m_ProjectionMatrix;
        private Matrix m_ViewMatrix;
        private Matrix m_PlotTransform;


        //zoom
        private PlotZoom m_PlotZoom = new PlotZoom();
        private const float m_fMinSubset = 0.05f;

        //PlotData
        private VertexBuffer m_vbPlotVertices;
        private IndexBuffer m_ibPlotIndices;
        private Material m_SurfaceMaterial;
        private Texture m_tGrayBox;
        private PlotType m_eCurrentPlotType = PlotType.None;
        List<int> m_LinePlotData = new List<int>();

        BackgroundWorker m_DataLoader = new BackgroundWorker();
        //Form m_LoadingForm;

        int m_iMzGrid;

        //////////////////////////////////////////////////////////////////////////
        //axis mesh
        private Mesh m_mAxisCylinder;
        private Mesh m_mAxisArrow;
        private Mesh m_mOrigin;

        //axis labels etc
        private string m_strMZLabel = "M/Z";
        private string m_strRTLabel = "Retention Time";
        private string m_strIntensityLabel = "Intensity";
        private Mesh m_MZLabelMesh;
        private Mesh m_RTLabelMesh;
        private Mesh m_IntensityLabelMesh;
        private const int m_NumMZMarkers = 9;
        private const int m_NumRTMarkers = 9;
        private const int m_NumIntensityMarkers = 5;
        private CustomVertex.PositionColored[] m_MZMarkerPoints = new CustomVertex.PositionColored[2 * m_NumMZMarkers];
        private CustomVertex.PositionColored[] m_RTMarkerPoints = new CustomVertex.PositionColored[2 * m_NumRTMarkers];
        private CustomVertex.PositionColored[] m_IntensityMarkerPoints = new CustomVertex.PositionColored[2 * m_NumIntensityMarkers];

        private List<Vector3> m_MZMarkerPositions = new List<Vector3>();
        private List<Vector3> m_RTMarkerPositions = new List<Vector3>();


        //////////////////////////////////////////////////////////////////////////
        /// COLORS 
        //////////////////////////////////////////////////////////////////////////
        private Color m_ColorBgNormal = Color.FromArgb(255, 40, 40, 40);
        private Color m_ColorBgCurrent = Color.FromArgb(255, 40, 40, 40);
        private Color m_ColorBgPrinter = Color.White;

        private Color m_ColorAxisNormal = Color.Black;
        private Color m_ColorAxisPrinter = Color.Black;
        private Color m_ColorAxisCurrent = Color.Black;

        private Color m_ColorFontNormal = Color.LightGray;
        private Color m_ColorFontPriner = Color.Black;
        private Color m_ColorFontCurrent = Color.LightGray;
        /////////////////////////////////////////////////////////////////////////
        private bool m_bRenderBoundingBox = true;
        private bool m_bIsLoading = false;
        private string m_ErrorText;



        private bool ErrorTextPresent
        {
            get
            {
                return !(m_ErrorText == null || m_ErrorText == "");
            }

        }

        private bool m_bSelectionMode = false;

        private Vector3 m_vSelectionStart;
        private Vector3 m_vSelectionEnd;

        private Effect m_effectSurface;
        private ContextMenuStrip contextMenuStrip1;
        private IContainer components;
        private ToolStripMenuItem zoomInToolStripMenuItem;
        private ToolStripMenuItem zoomOutToolStripMenuItem;
        private ToolStripSeparator toolStripSeparator1;
        private ToolStripMenuItem saveAsToolStripMenuItem;
        /// <summary>
        /// Selection mode enables user to draw a rectangle on the ground to select a subplot
        /// </summary>
        public bool SelectionMode
        {
            get { return m_bSelectionMode; }
            set
            {
                m_bSelectionMode = value;
            }
        }
        private bool m_bSelectionStarted = false;



        public bool IsLoading
        {
            get { return m_bIsLoading; }
            set
            {
                m_bIsLoading = value;
                Refresh();
            }
        }


        public float CurrentCameraDistance
        {
            get { return m_fCurrentCameraDistance; }
            set
            {
                m_fCurrentCameraDistance = value;
                if (m_fCurrentCameraDistance > m_fMaxCameraDistance)
                    m_fCurrentCameraDistance = m_fMaxCameraDistance;
                else if (m_fCurrentCameraDistance < m_fMinCameraDistance)
                    m_fCurrentCameraDistance = m_fMinCameraDistance;

                SetCamera();
                if (m_PlotData != null && m_PlotData.PlotSettings.ViewSettings.ProjectionType == ProjectionType.Orthographic)
                    SetProjection();    //
            }
        }
        /// <summary>
        /// Sets camera zoom, in percents.
        /// </summary>
        /// <param name="zoom">Zoom in percents - 0 means no zoom, 100 means maximum possible zoom.</param>
        public void SetCameraZoom(int zoom)
        {
            CurrentCameraDistance = m_fMaxCameraDistance - (m_fMaxCameraDistance - m_fMinCameraDistance) * zoom / 100.0f;
            Refresh();
        }


        struct AxisMarker
        {
            public float m_fValue;


            public Mesh m_Text3D;
            public Vector3 m_vPosition;
        }

        private AxisMarker[] m_MZMarkers = new AxisMarker[m_NumMZMarkers];
        private AxisMarker[] m_RTMarkers = new AxisMarker[m_NumRTMarkers];
        private AxisMarker[] m_IntensityMarkers = new AxisMarker[m_NumIntensityMarkers];
        private Microsoft.DirectX.Direct3D.Font m_axisFont;

        //bounding box
        private VertexBuffer m_vbPlotArea;
        private VertexBuffer m_vbSelection;
        private IndexBuffer m_ibPlotAreaBBTriangles;
        private IndexBuffer m_ibPlotAreaBBLines;
        private float m_bbAreaScale = 1.01f;




        //////////////////////////////////////////////////////////////////////////
        //Plot data
        InternalPlotData m_PlotData;

        public InternalPlotData PlotData
        {
            get { return m_PlotData; }
        }
        //////////////////////////////////////////////////////////////////////////
        //Vertex selection

        private Mesh m_mVertexSelectionMesh;
        private float m_fSelectionIntensityTreshold = 0.1f;
        private float m_fSelectionSnapDistance = 0.01f;

        private bool m_bVertexSelected = false;
        private bool m_bDoubleSelection = false;
        private DataPoint m_SelectedData;
        private float m_fSelectedIntensity;
        private Vector3 m_vSelectedPosition;
        private bool m_bEnableVertexSelection = true;

        struct SelectedVertexData
        {
            public Vector3 m_vPosition;
            public DataPoint m_DataPoint;
            public float m_fRetentionTime;
        }

        List<SelectedVertexData> m_SelectedPoints = new List<SelectedVertexData>();

        //////////////////////////////////////////////////////////////////////////
        private Mesh m_mPoint;
        private Texture m_GradientTexture;
        private Texture m_DashedLineTexture;
        private Texture m_WhiteTexture;
        private Material m_mMaterial;


        //////////////////////////////////////////////////////////////////////////
        public Plot3DControl()
            : base()
        {

            BuildAxisMarkerPoints();
            InitializeComponent();


        }



        /// <summary>
        /// 
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.zoomInToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.zoomOutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
            this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.contextMenuStrip1.SuspendLayout();
            this.SuspendLayout();
            // 
            // contextMenuStrip1
            // 
            this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.zoomInToolStripMenuItem,
            this.zoomOutToolStripMenuItem,
            this.toolStripSeparator1,
            this.saveAsToolStripMenuItem});
            this.contextMenuStrip1.Name = "contextMenuStrip1";
            this.contextMenuStrip1.Size = new System.Drawing.Size(153, 98);
            // 
            // zoomInToolStripMenuItem
            // 
            this.zoomInToolStripMenuItem.Name = "zoomInToolStripMenuItem";
            this.zoomInToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.zoomInToolStripMenuItem.Text = "Zoom in";
            this.zoomInToolStripMenuItem.Click += new System.EventHandler(this.zoomInToolStripMenuItem_Click);
            // 
            // zoomOutToolStripMenuItem
            // 
            this.zoomOutToolStripMenuItem.Name = "zoomOutToolStripMenuItem";
            this.zoomOutToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.zoomOutToolStripMenuItem.Text = "Zoom out";
            this.zoomOutToolStripMenuItem.Click += new System.EventHandler(this.zoomOutToolStripMenuItem_Click);
            // 
            // toolStripSeparator1
            // 
            this.toolStripSeparator1.Name = "toolStripSeparator1";
            this.toolStripSeparator1.Size = new System.Drawing.Size(149, 6);
            // 
            // saveAsToolStripMenuItem
            // 
            this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
            this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.saveAsToolStripMenuItem.Text = "Save As";
            this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click);
            // 
            // Plot3DControl
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.Name = "Plot3DControl";
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Plot3DControl_MouseMove);
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Plot3DControl_MouseDown);
            this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Plot3DControl_MouseUp);
            this.contextMenuStrip1.ResumeLayout(false);
            this.ResumeLayout(false);
            //this.ContextMenuStrip = contextMenuStrip1;

        }

        #region Utility Methods

        private float m_fSelectedVertexSnapDistance = 10f;
        private bool CheckForDoubleSelection(int mouseX, int mouseY, out Vector3 alreadySelectedVertex)
        {
            alreadySelectedVertex = new Vector3();
            for (int i = 0; i < m_SelectedPoints.Count; i++)
            {
                float fDist;
                Vector3 transformedPos = m_SelectedPoints[i].m_vPosition;
                transformedPos.TransformCoordinate(m_PlotTransform);

                if (transformedPos.X >= -m_vPlotArea.X / 2 && transformedPos.X <= m_vPlotArea.X / 2 &&
                        transformedPos.Z >= -m_vPlotArea.Z / 2 && transformedPos.Z <= m_vPlotArea.Z / 2)
                {
                    Vector2 screenPos = WorldToScreen(transformedPos);

                    screenPos.X -= mouseX;
                    screenPos.Y -= mouseY;
                    if (screenPos.Length() < m_fSelectedVertexSnapDistance)
                    {
                        alreadySelectedVertex = m_SelectedPoints[i].m_vPosition;
                        return true;
                    }
                }

                //if (dist < m_fSelectedVertexSnapDistance)
                //{
                //    alreadySelectedVertex = m_SelectedPoints[i].m_vPosition;
                //    return true;
                //}
            }
            return false;
        }

        private float CalculateRayPointDistance(Vector3 rayFrom, Vector3 rayDir, Vector3 point, out float distanceToRayFrom)
        {
            distanceToRayFrom = Vector3.Dot(rayDir, point - rayFrom) / rayDir.LengthSq();
            float dist = (point - (rayFrom + distanceToRayFrom * rayDir)).Length();

            return dist;
        }

        private float GetNewRoundNumber(float number, float precision)
        {
            number += precision;
            float remainder = number - (int)(number / precision) * precision;
            return number - remainder;
        }

        private float GetRoundNumber(float number, float precision)
        {
            if (number - (int)(number / precision) * precision == 0)
                return number;
            else
                return GetNewRoundNumber(number, precision);
        }

        void BuildAxisLabels()
        {

            //axis label meshes
            if (m_MZLabelMesh != null)
                m_MZLabelMesh.Dispose();
            if (m_RTLabelMesh != null)
                m_MZLabelMesh.Dispose();
            if (m_IntensityLabelMesh != null)
                m_IntensityLabelMesh.Dispose();

            m_MZLabelMesh = Mesh.TextFromFont(d3dDevice, this.Font, m_strMZLabel, 0.01f, 0.01f);
            m_RTLabelMesh = Mesh.TextFromFont(d3dDevice, this.Font, m_strRTLabel, 0.01f, 0.01f);
            if (m_PlotData != null && m_PlotData.PlotSettings.LogarithmicIntensity)
                m_IntensityLabelMesh = Mesh.TextFromFont(d3dDevice, this.Font, m_strIntensityLabel + "   [log]", 0.01f, 0.01f);
            else
                m_IntensityLabelMesh = Mesh.TextFromFont(d3dDevice, this.Font, m_strIntensityLabel, 0.01f, 0.01f);
        }

        void BuildDashedLine()
        {
            if (m_DashedLineTexture != null && !m_DashedLineTexture.Disposed)
            {
                m_DashedLineTexture.Dispose();
            }

            if (m_WhiteTexture != null && !m_WhiteTexture.Disposed)
            {
                m_WhiteTexture.Dispose();
            }

            Bitmap dashedLineBitmap = null;

            int dashedLineResolution = 256;
            dashedLineBitmap = new Bitmap(dashedLineResolution, 1, PixelFormat.Format32bppArgb);
            BitmapData bmpData = dashedLineBitmap.LockBits(new Rectangle(0, 0, dashedLineResolution, 1), System.Drawing.Imaging.ImageLockMode.WriteOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            IntPtr ptr = bmpData.Scan0;
            int[] argbData = new int[dashedLineResolution];

            for (int i = 0; i < dashedLineResolution; i++)
            {
                if (i < dashedLineResolution / 2)
                    argbData[i] = Color.FromArgb(255, 90, 90, 90).ToArgb();
                else
                    argbData[i] = Color.FromArgb(0, 0, 0, 0).ToArgb();
            }
            System.Runtime.InteropServices.Marshal.Copy(argbData, 0, ptr, argbData.Length);
            dashedLineBitmap.UnlockBits(bmpData);

            //dashedLineBitmap.Save("dashed.bmp");
            m_DashedLineTexture = new Texture(d3dDevice, dashedLineBitmap, Usage.None, Pool.Managed);

            Bitmap whiteBitmap = null;
            whiteBitmap = new Bitmap(2, 2, PixelFormat.Format32bppArgb);
            bmpData = whiteBitmap.LockBits(new Rectangle(0, 0, 2, 2), System.Drawing.Imaging.ImageLockMode.WriteOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            ptr = bmpData.Scan0;
            argbData = new int[4];

            for (int i = 0; i < 4; i++)
            {
                argbData[i] = Color.White.ToArgb();
            }
            System.Runtime.InteropServices.Marshal.Copy(argbData, 0, ptr, argbData.Length);
            whiteBitmap.UnlockBits(bmpData);

            //dashedLineBitmap.Save("dashed.bmp");
            m_WhiteTexture = new Texture(d3dDevice, whiteBitmap, Usage.None, Pool.Managed);
        }


        /// <summary>
        /// Builds a texture with color gradient for the plot
        /// </summary>
        void BuildGradientTexture()
        {
            if (m_GradientTexture != null && !m_GradientTexture.Disposed)
                m_GradientTexture.Dispose();

            const int gradientResolution = 512;

            Bitmap colorGradientBmp = new Bitmap(gradientResolution, 1, PixelFormat.Format32bppArgb);
            BitmapData bmpData = colorGradientBmp.LockBits(new Rectangle(0, 0, gradientResolution, 1), System.Drawing.Imaging.ImageLockMode.WriteOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            IntPtr ptr = bmpData.Scan0;
            int[] argbData = new int[gradientResolution];

            for (int i = 0; i < gradientResolution; i++)
            {
                Color color = m_PlotData.PlotSettings.ColorGradient.GetColor((float)i / (gradientResolution - 1));

                argbData[i] = color.ToArgb();
            }
            System.Runtime.InteropServices.Marshal.Copy(argbData, 0, ptr, argbData.Length);
            colorGradientBmp.UnlockBits(bmpData);


            m_GradientTexture = new Texture(d3dDevice, colorGradientBmp, Usage.None, Pool.Managed);
        }

        /// <summary>
        /// Current MZ axis minimum
        /// </summary>
        float AxisMinMZ
        {
            get
            {
                return m_PlotData.MinMZ + m_PlotZoom.m_fMZOffset * m_PlotData.MZRange;
            }
        }
        /// <summary>
        /// Current MZ axis maximum
        /// </summary>
        float AxisMaxMZ
        {
            get
            {
                return m_PlotData.MinMZ + (m_PlotZoom.m_fMZOffset + m_PlotZoom.m_fMZSubset) * m_PlotData.MZRange;
            }
        }

        /// <summary>
        /// Current RT axis minimum
        /// </summary>
        float AxisMinRT
        {
            get
            {
                return m_PlotData.MinRetentionTime + m_PlotZoom.m_fRTOffset * m_PlotData.RTRange;
            }
        }
        /// <summary>
        /// Current RT axis maximum
        /// </summary>
        float AxisMaxRT
        {
            get
            {
                return m_PlotData.MinRetentionTime + (m_PlotZoom.m_fRTOffset + m_PlotZoom.m_fRTSubset) * m_PlotData.RTRange;
            }
        }

        /// <summary>
        /// Converts world(3D) coordinates to screen(2D) coordinates
        /// </summary>
        /// <param name="vPos"></param>
        /// <returns></returns>
        private Vector2 WorldToScreen(Vector3 vPos)
        {
            vPos.Project(d3dDevice.Viewport, m_ProjectionMatrix, m_ViewMatrix, Matrix.Identity);
            Vector2 result;
            result.X = vPos.X;
            result.Y = vPos.Y;
            return result;
        }

        /// <summary>
        /// Converts world(3D) coordinates to screen(2D) coordinates
        /// </summary>
        /// <param name="vPos"></param>
        /// <returns></returns>
        private Vector2 WorldToScreen(Vector3 vPos, Matrix world)
        {
            vPos.Project(d3dDevice.Viewport, m_ProjectionMatrix, m_ViewMatrix, world);
            Vector2 result;
            result.X = vPos.X;
            result.Y = vPos.Y;
            return result;
        }


        /// <summary>
        /// Builds axis markers
        /// </summary>
        void BuildAxisMarkerPoints()
        {

            Vector3 vbbArea = m_vPlotArea * m_bbAreaScale;
            Vector3 vOrigin = m_vPlotPosition - new Vector3(vbbArea.X, 0, -vbbArea.Z) * 0.5f;

            Vector3 vRTEnd = vOrigin;
            vRTEnd.X += m_vPlotArea.X;
            Vector3 vMZEnd = vOrigin;
            vMZEnd.Z += m_vPlotArea.Z;



            for (int i = 0; i < 2 * m_NumRTMarkers; i += 2)
            {
                m_RTMarkerPoints[i] = new CustomVertex.PositionColored();
                m_RTMarkerPoints[i + 1] = new CustomVertex.PositionColored();
                m_RTMarkerPoints[i].Color = Color.Black.ToArgb();
                m_RTMarkerPoints[i + 1].Color = Color.Black.ToArgb();
                m_RTMarkerPoints[i].Position = vOrigin + new Vector3(vbbArea.X * 0.5f * i / (m_NumRTMarkers - 1), 0, 0);
                if (i == m_NumRTMarkers - 1 || i / 2 == 0 || i / 2 == m_NumRTMarkers - 1)
                    m_RTMarkerPoints[i + 1].Position = vOrigin + new Vector3(vbbArea.X * 0.5f * i / (m_NumRTMarkers - 1), 0, 0) + new Vector3(0, -2, 10);
                else
                    m_RTMarkerPoints[i + 1].Position = vOrigin + new Vector3(vbbArea.X * 0.5f * i / (m_NumRTMarkers - 1), 0, 0) + new Vector3(0, -1, 5);

            }

            for (int i = 0; i < 2 * m_NumMZMarkers; i += 2)
            {
                m_MZMarkerPoints[i] = new CustomVertex.PositionColored();
                m_MZMarkerPoints[i + 1] = new CustomVertex.PositionColored();
                m_MZMarkerPoints[i].Color = Color.Black.ToArgb();
                m_MZMarkerPoints[i + 1].Color = Color.Black.ToArgb();
                m_MZMarkerPoints[i].Position = vOrigin - new Vector3(0, 0, vbbArea.Z * 0.5f * i / (m_NumMZMarkers - 1));
                if (i == m_NumMZMarkers - 1 || i / 2 == 0 || i / 2 == m_NumMZMarkers - 1)
                    m_MZMarkerPoints[i + 1].Position = vOrigin - new Vector3(0, 0, vbbArea.Z * 0.5f * i / (m_NumMZMarkers - 1)) + new Vector3(-10, -2, 0);
                else
                    m_MZMarkerPoints[i + 1].Position = vOrigin - new Vector3(0, 0, vbbArea.Z * 0.5f * i / (m_NumMZMarkers - 1)) + new Vector3(-5, -1, 0);

            }

            for (int i = 0; i < 2 * m_NumIntensityMarkers; i += 2)
            {
                m_IntensityMarkerPoints[i] = new CustomVertex.PositionColored();
                m_IntensityMarkerPoints[i + 1] = new CustomVertex.PositionColored();
                m_IntensityMarkerPoints[i].Color = Color.Black.ToArgb();
                m_IntensityMarkerPoints[i + 1].Color = Color.Black.ToArgb();
                m_IntensityMarkerPoints[i].Position = vOrigin + new Vector3(0, vbbArea.Y * 0.5f * i / (m_NumIntensityMarkers - 1), 0);
                if (i == m_NumIntensityMarkers - 1 || i / 2 == 0 || i / 2 == m_NumIntensityMarkers - 1)
                    m_IntensityMarkerPoints[i + 1].Position = vOrigin + new Vector3(0, vbbArea.Y * 0.5f * i / (m_NumIntensityMarkers - 1), 0) + new Vector3(-7, 0, 7);
                else
                    m_IntensityMarkerPoints[i + 1].Position = vOrigin + new Vector3(0, vbbArea.Y * 0.5f * i / (m_NumIntensityMarkers - 1), 0) + new Vector3(-3, 0, 3);

            }
        }

        /// <summary>
        /// Not used
        /// </summary>
        void BuildAxisMarkers()
        {
            float fMZStep = m_PlotData.MZRange / (m_NumMZMarkers - 1);
            for (int i = 0; i < m_NumMZMarkers; i++)
            {
                m_MZMarkers[i] = new AxisMarker();
                m_MZMarkers[i].m_fValue = m_PlotData.MinMZ + i * fMZStep;
                m_MZMarkers[i].m_Text3D = Mesh.TextFromFont(d3dDevice, this.Font, m_MZMarkers[i].m_fValue.ToString(), 0.1f, 0.1f);
                m_MZMarkers[i].m_vPosition = m_MZMarkerPoints[2 * i].Position + new Vector3(-12, -2, 0);

            }

            float fRTStep = m_PlotData.RTRange / (m_NumRTMarkers - 1);
            for (int i = 0; i < m_NumRTMarkers; i++)
            {
                m_RTMarkers[i] = new AxisMarker();
                m_RTMarkers[i].m_fValue = m_PlotData.MinRetentionTime + i * fRTStep;
                m_RTMarkers[i].m_Text3D = Mesh.TextFromFont(d3dDevice, this.Font, m_RTMarkers[i].m_fValue.ToString(), 0.1f, 0.1f);
                m_RTMarkers[i].m_vPosition = m_RTMarkerPoints[2 * i].Position + new Vector3(0, -2, 12);

            }

            float fIntensityStep = m_PlotData.MaxIntensity / (m_NumIntensityMarkers - 1);
            for (int i = 0; i < m_NumIntensityMarkers; i++)
            {
                m_IntensityMarkers[i] = new AxisMarker();
                m_IntensityMarkers[i].m_fValue = i * fIntensityStep;
                m_IntensityMarkers[i].m_Text3D = Mesh.TextFromFont(d3dDevice, this.Font, m_IntensityMarkers[i].m_fValue.ToString(), 0.1f, 0.1f);
                m_IntensityMarkers[i].m_vPosition = m_IntensityMarkerPoints[2 * i].Position + new Vector3(-12, 5, 12);

            }
        }

        #endregion

        #region Data methods


        /// <summary>
        /// Setts data for the Plot3D
        /// </summary>
        /// <param name="arrList">Plot data struct filled with samples to plot</param>
        public void SetData(InternalPlotData plotData)
        {
            if (d3dDevice == null) return;

            m_ErrorText = "";//no error, for now
            if (plotData == null)
                debugLog.Write("Setting new data...null!");
            else
            {
                switch (plotData.PlotSettings.PlotType)
                {
                    case PlotType.Points:
                        debugLog.Write("Setting new Data...Point plot");
                        break;
                    case PlotType.Lines:
                        debugLog.Write("Setting new Data...Lines plot");
                        break;
                    case PlotType.Surface:
                        debugLog.Write("Setting new Data...Surface plot");
                        break;
                }
            }

            m_PlotData = plotData;
            //m_PlotZoom.Reset();

            //m_iCurrentZoomMZ = 0;
            //m_iCurrentZoomRT = 0;
            //m_iCurrentScrollZoom = 0;

            IsLoading = true;   //display loading screen
            m_ColorBgNormal = plotData.PlotSettings.ViewSettings.BkgColor;
            SetColorMode(plotData.PlotSettings.ViewSettings.PrinterColors);


            m_bVertexSelected = false;
            m_SelectedPoints.Clear();


            //build plot
            BuildPlot();


            //Refresh();

        }


        /// <summary>
        /// Builds a uniform grid of vertices from plot data
        /// </summary>
        /// <param name="iRtCount"></param>
        /// <param name="iMzCount"></param>
        /// <returns></returns>
        private CustomVertex.PositionNormalTextured[] BuildUniformGrid(int iRtCount, int iMzCount, out int[] aIndices)
        {
            if (m_PlotData.RetentionScansCount == 0)
            {
                //nothing to build, return
                aIndices = null;
                return null;
            }
            iRtCount = m_PlotData.RetentionScansCount;
            float fMzStep = (m_PlotData.MaxMZ - m_PlotData.MinMZ) / (iMzCount - 1);

            CustomVertex.PositionNormalTextured[] aVertices = new CustomVertex.PositionNormalTextured[iMzCount * iRtCount];

            int iCurrentRtIndex = 0;


            float fMZResolution = 1;// m_PlotData.MZRange / m_PlotData.PlotSettings.PlotSubsetDescription.m_iResolutionMZ;


            while (iCurrentRtIndex < m_PlotData.RetentionScansCount)
            {
                int iCurrentMzIndex = 0;
                int iCurrentRowVertex = 0;
                float fCurrentOffset = m_PlotData.MinMZ;

                Spectrum rowData = m_PlotData.PlotSamples[iCurrentRtIndex];

                float fDefaultValue = 0;
                int iStartingVertex = iCurrentRtIndex * iMzCount;

                while (iCurrentRowVertex < iMzCount)
                {
                    //check current group(from fCurrentOffset to fCurrentOffset+fMzStep)
                    float fMaxIntensity = 0;
                    int iMaxIntensityIndex = -1;
                    while (iCurrentMzIndex < rowData.NumberOfSamples && rowData.m_PD[iCurrentMzIndex].MZ <= fCurrentOffset + (1 + iCurrentRowVertex) * fMzStep)
                    {
                        if (rowData.m_PD[iCurrentMzIndex].Intensity > fMaxIntensity)
                        {
                            fMaxIntensity = rowData.m_PD[iCurrentMzIndex].Intensity;
                            iMaxIntensityIndex = iCurrentMzIndex;
                        }
                        iCurrentMzIndex++;
                    }

                    float fVMz, fVIntensity;
                    if (iMaxIntensityIndex > 0)
                    {
                        fVMz = rowData.m_PD[iMaxIntensityIndex].MZ;
                        fVIntensity = fMaxIntensity;

                        fDefaultValue = fMaxIntensity;
                    }
                    else
                    {
                        fVMz = fCurrentOffset + fMzStep * iCurrentRowVertex;//fMzStep / 2;
                        fVIntensity = -1;
                    }
                    Vector3 pos = new Vector3(rowData.m_RT, fVIntensity, fVMz);

                    //add new vertex
                    aVertices[iCurrentRtIndex * iMzCount + iCurrentRowVertex] = new CustomVertex.PositionNormalTextured(new Vector3(pos.X,
                        pos.Y, pos.Z), new Vector3(), fVIntensity / m_PlotData.MaxIntensity, 0);

                    iCurrentRowVertex++;

                }

                int iLastNonZeroVertex = -1;
                int iNextNonZeroVertex = -1;
                //check for grid points with intensity below 0, and treat them appropriately
                for (int i = iStartingVertex; i < iStartingVertex + iMzCount; i++)
                {
                    if (aVertices[i].Y != -1)
                    {
                        iLastNonZeroVertex = i;
                        iNextNonZeroVertex = -1;
                    }
                    else if (iNextNonZeroVertex == -1)
                    {
                        //find current group, if it is wider that fMZResolution then make all values zeros
                        int j;
                        for (j = i + 1; j < iStartingVertex + iMzCount; j++)
                        {
                            if (aVertices[j].Y != -1)
                            {
                                iNextNonZeroVertex = j;
                                break;
                            }
                        }
                        if (j == iStartingVertex + iMzCount)
                        {
                            //no good vertices till the end of line
                            if (iLastNonZeroVertex != -1)
                            {
                                if (m_PlotData.MaxMZ - aVertices[iLastNonZeroVertex].Z < fMZResolution)
                                {
                                    int iCurrentIndex = iLastNonZeroVertex + 1;
                                    while (iCurrentIndex < iStartingVertex + iMzCount)
                                    {
                                        aVertices[iCurrentIndex].Y = aVertices[iLastNonZeroVertex].Y;
                                        aVertices[iCurrentIndex].Tu = aVertices[iCurrentIndex].Y / m_PlotData.MaxIntensity;
                                        iCurrentIndex++;
                                    }
                                }
                                else
                                {
                                    int iCurrentIndex = iLastNonZeroVertex + 1;
                                    while (iCurrentIndex < iStartingVertex + iMzCount)
                                    {
                                        aVertices[iCurrentIndex].Y = 0;
                                        aVertices[iCurrentIndex].Tu = 0;
                                        iCurrentIndex++;
                                    }
                                }
                            }
                            else
                            {

                                //no data in line
                                int iCurrentIndex = iStartingVertex;
                                while (iCurrentIndex < iStartingVertex + iMzCount)
                                {
                                    aVertices[iCurrentIndex].Y = 0;
                                    aVertices[iCurrentIndex].Tu = 0;
                                    iCurrentIndex++;
                                }

                            }

                        }
                        else
                        {

                            if (iLastNonZeroVertex != -1)
                            {
                                //internal group, interpolate
                                float fMinMZ = aVertices[iLastNonZeroVertex].Z;
                                float fMinInt = aVertices[iLastNonZeroVertex].Y;

                                float fMaxMZ = aVertices[iNextNonZeroVertex].Z;
                                float fMaxInt = aVertices[iNextNonZeroVertex].Y;
                                if (fMaxMZ - fMinMZ < fMZResolution)
                                {
                                    for (int iCurrentIndex = iLastNonZeroVertex + 1; iCurrentIndex < iNextNonZeroVertex; iCurrentIndex++)
                                    {
                                        aVertices[iCurrentIndex].Y = fMinInt + (aVertices[iCurrentIndex].Z - fMinMZ) / (fMaxMZ - fMinMZ) * (fMaxInt - fMinInt);
                                        aVertices[iCurrentIndex].Tu = aVertices[iCurrentIndex].Y / m_PlotData.MaxIntensity;
                                    }
                                }
                                else
                                {
                                    for (int iCurrentIndex = iLastNonZeroVertex + 1; iCurrentIndex < iNextNonZeroVertex; iCurrentIndex++)
                                    {
                                        aVertices[iCurrentIndex].Y = 0;
                                        aVertices[iCurrentIndex].Tu = 0;
                                    }
                                }

                            }
                            else
                            {

                                if (aVertices[iNextNonZeroVertex].Z - m_PlotData.MinMZ < fMZResolution)
                                {
                                    int iCurrentIndex = iStartingVertex;
                                    while (iCurrentIndex < iNextNonZeroVertex)
                                    {
                                        aVertices[iCurrentIndex].Y = aVertices[iNextNonZeroVertex].Y;
                                        aVertices[iCurrentIndex].Tu = aVertices[iNextNonZeroVertex].Tu;
                                        iCurrentIndex++;
                                    }
                                }
                                else
                                {
                                    int iCurrentIndex = iStartingVertex;
                                    while (iCurrentIndex < iNextNonZeroVertex)
                                    {
                                        aVertices[iCurrentIndex].Y = 0;
                                        aVertices[iCurrentIndex].Tu = 0;
                                        iCurrentIndex++;
                                    }
                                }

                            }

                        }
                    }
                }
                iCurrentRtIndex++;

            }


            int nX = iMzCount;
            int nY = m_PlotData.RetentionScansCount;
            int nIndices = 6 * (nX - 1) * (nY - 1);
            int[] indices = new int[nIndices];
            int index = 0;
            for (int y = 0; y < nY - 1; y++)
            {
                int iLowerBase = y * nX;

                for (int x = 0; x < iMzCount - 1; x++)
                {
                    indices[index++] = iLowerBase + x;
                    indices[index++] = iLowerBase + x + nX;
                    indices[index++] = iLowerBase + x + 1;


                    indices[index++] = iLowerBase + x + 1;
                    indices[index++] = iLowerBase + x + nX;
                    indices[index++] = iLowerBase + x + nX + 1;

                }
            }

            aIndices = indices;
            return aVertices;
        }

        /// <summary>
        /// builds a new plot according to current settings
        /// </summary>
        private void BuildPlot()
        {
            if (m_PlotData.RetentionScansCount == 0)
            {
                m_ErrorText = "\n\n\nNo data to display, check plot settings!\n(probably not enough memory)";
                debugLog.Write(m_ErrorText);
                return;
            }
            else
                m_ErrorText = null;

            debugLog.Write("Building plot...");

            m_DataLoader = new BackgroundWorker();


            if (m_PlotData.PlotSettings.PlotType == PlotType.Points)
            {
                //aVertices = BuildPointsPlot(out aIndices);
                m_DataLoader.DoWork += new DoWorkEventHandler(m_DataLoader_BuildPointsPlot);
            }
            else if (m_PlotData.PlotSettings.PlotType == PlotType.Lines)
            {
                //aVertices = BuildLinesPlot(out aIndices);
                m_DataLoader.DoWork += new DoWorkEventHandler(m_DataLoader_BuildLinesPlot);
            }
            else //(m_PlotData.PlotSettings.PlotType == PlotTypes.Surface)
            {
                //aVertices = BuildSurfacePlot(out aIndices);

                m_DataLoader.DoWork += new DoWorkEventHandler(m_DataLoader_BuildSurfacePlot);
            }

            m_DataLoader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_DataLoader_RunWorkerCompleted);
            m_DataLoader.RunWorkerAsync();


        }

        void BuildDirectXData(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled || e.Error != null)
            {
                debugLog.Write("Exception while creating plot data!");
                m_ErrorText = "\n\n\nException while creating plot data!";
                Refresh();
                return;
            }
            if (m_vbPlotVertices != null)
                m_vbPlotVertices.Dispose();
            if (m_ibPlotIndices != null)
                m_ibPlotIndices.Dispose();
            m_vbPlotVertices = null;
            m_ibPlotIndices = null;

            debugLog.Write("Background worker finished preparing data, now creating vertex and index buffers");

            int[] aIndices = (int[])((object[])e.Result)[1];
            m_eCurrentPlotType = PlotType.None;
            try
            {



                if ((PlotType)((object[])e.Result)[2] == PlotType.Points)
                {
                    if (((object[])e.Result)[0] == null)
                        throw new OutOfMemoryException();
                    CustomVertex.PositionTextured[] aVertices = (CustomVertex.PositionTextured[])((object[])e.Result)[0];

                    //create direct3d data
                    m_vbPlotVertices = new VertexBuffer(typeof(CustomVertex.PositionTextured), aVertices.Length, d3dDevice, Usage.WriteOnly, CustomVertex.PositionTextured.Format, Pool.Managed);
                    m_vbPlotVertices.SetData(aVertices, 0, LockFlags.None);
                }
                else if ((PlotType)((object[])e.Result)[2] == PlotType.Lines)
                {
                    if (((object[])e.Result)[0] == null)
                        throw new OutOfMemoryException();
                    CustomVertex.PositionColored[] aVertices = (CustomVertex.PositionColored[])((object[])e.Result)[0];

                    //create direct3d data
                    m_vbPlotVertices = new VertexBuffer(typeof(CustomVertex.PositionColored), aVertices.Length, d3dDevice, Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Managed);
                    m_vbPlotVertices.SetData(aVertices, 0, LockFlags.None);
                }
                else if ((PlotType)((object[])e.Result)[2] == PlotType.Surface)
                {
                    if (((object[])e.Result)[0] == null)
                        throw new OutOfMemoryException();
                    CustomVertex.PositionNormalTextured[] aVertices = (CustomVertex.PositionNormalTextured[])((object[])e.Result)[0];

                    //create direct3d data
                    m_vbPlotVertices = new VertexBuffer(typeof(CustomVertex.PositionNormalTextured), aVertices.Length,
                        d3dDevice, Usage.WriteOnly, CustomVertex.PositionNormalTextured.Format, Pool.Managed);
                    m_vbPlotVertices.SetData(aVertices, 0, LockFlags.None);
                }

                if (aIndices != null)
                {
                    m_ibPlotIndices = new IndexBuffer(typeof(int), aIndices.Length, d3dDevice, Usage.WriteOnly, Pool.Managed);
                    m_ibPlotIndices.SetData(aIndices, 0, LockFlags.None);
                }
                m_eCurrentPlotType = (PlotType)((object[])e.Result)[2];

            }
            catch (OutOfMemoryException)
            {
                if (m_vbPlotVertices != null)
                    m_vbPlotVertices.Dispose();
                if (m_ibPlotIndices != null)
                    m_ibPlotIndices.Dispose();
                m_vbPlotVertices = null;
                m_ibPlotIndices = null;

                //MessageBox.Show("Not enough memory!");
                debugLog.Write("Not enough memory!");
                m_ErrorText = "\n\n\nNot enough system memory, try different settings!";
                Refresh();
                return;

            }
            catch (OutOfVideoMemoryException)
            {
                if (m_vbPlotVertices != null)
                    m_vbPlotVertices.Dispose();
                if (m_ibPlotIndices != null)
                    m_ibPlotIndices.Dispose();
                m_vbPlotVertices = null;
                m_ibPlotIndices = null;

                //MessageBox.Show("Not enough video memory!");
                debugLog.Write("Not enough video memory!");
                m_ErrorText = "\n\n\nNot enough video memory, try different settings!";

                Refresh();
                return;
            }


            //build gradient texture
            BuildGradientTexture();
            //update axis markers labels
            BuildAxisMarkers();

            //Update Axis Text
            BuildAxisLabels();

            //loading complete
            m_bIsLoading = false;
            debugLog.Write("Finished creating directx data");

            //make sure we have correct projection
            SetProjection();
            //redraw
            Refresh();
        }

        /// <summary>
        /// Constructs vertex and index buffers with plot data, called when background worker
        /// finishes preparing vertices and indices
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_DataLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            RunWorkerCompletedEventHandler del = new RunWorkerCompletedEventHandler(BuildDirectXData);
            this.BeginInvoke(del, new object[] { sender, e });
        }

        /// <summary>
        /// Builds graphics data for points plot-called by a background worker
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_DataLoader_BuildPointsPlot(object sender, DoWorkEventArgs e)
        {
            try
            {
                int[] aIndices = null;
                CustomVertex.PositionTextured[] aVertices = null;
                aVertices = BuildPointsPlot(out aIndices);
                object[] res = new object[3];
                res[0] = aVertices;
                res[1] = aIndices;
                res[2] = PlotType.Points;
                e.Result = res;

            }
            catch (OutOfMemoryException)
            {
                debugLog.Write("OutOfMemoryException in BuildPointsPlot");
                object[] res = new object[3];
                res[0] = null;
                res[1] = null;
                res[2] = PlotType.Points;
                e.Result = res;
            }

        }

        /// <summary>
        /// Builds graphics data for lines plot-called by a background worker
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_DataLoader_BuildLinesPlot(object sender, DoWorkEventArgs e)
        {
            try
            {
                int[] aIndices = null;
                CustomVertex.PositionColored[] aVertices = null;
                aVertices = BuildLinesPlot(out aIndices);
                object[] res = new object[3];
                res[0] = aVertices;
                res[1] = aIndices;
                res[2] = PlotType.Lines;
                e.Result = res;
            }
            catch (OutOfMemoryException)
            {
                debugLog.Write("OutOfMemoryException in BuildLinesPlot");
                object[] res = new object[3];
                res[0] = null;
                res[1] = null;
                res[2] = PlotType.Lines;
                e.Result = res;
            }

        }

        /// <summary>
        /// Builds graphics data for surface plot-called by a background worker
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_DataLoader_BuildSurfacePlot(object sender, DoWorkEventArgs e)
        {
            try
            {
                int[] aIndices = null;
                CustomVertex.PositionNormalTextured[] aVertices = null;
                aVertices = BuildSurfacePlot(out aIndices);
                object[] res = new object[4];
                res[0] = aVertices;
                res[1] = aIndices;
                res[2] = PlotType.Surface;
                e.Result = res;
            }
            catch (OutOfMemoryException)
            {
                debugLog.Write("OutOfMemoryException in BuildSurfacePlot");
                object[] res = new object[3];
                res[0] = null;
                res[1] = null;
                res[2] = PlotType.Surface;
                e.Result = res;
            }

        }

        /// <summary>
        /// Changes plot type
        /// </summary>
        /// <param name="newPlotType"></param>
        public void UpdatePlot(PlotType newPlotType)
        {
            if (d3dDevice == null) return;
            if (m_PlotData == null) return;
            if (newPlotType != m_PlotData.PlotSettings.PlotType)
            {
                m_PlotData.PlotSettings.PlotType = newPlotType;
                UpdatePlot(m_PlotData.PlotSettings);
            }
        }
        /// <summary>
        /// Updates current plot using new settings
        /// </summary>
        /// <param name="newSettings"></param>
        public void UpdatePlot(InternalPlotSettings newSettings)
        {
            if (d3dDevice == null) return;
            m_ErrorText = "";

            IsLoading = true;
            m_PlotData.PlotSettings = (InternalPlotSettings)newSettings.Clone();
            m_ColorBgNormal = newSettings.ViewSettings.BkgColor;
            SetColorMode(newSettings.ViewSettings.PrinterColors);
            BuildPlot();

            SetProjection();
            Refresh();

        }

        /// <summary>
        /// Builds vertices and indices for lines plot
        /// </summary>
        /// <param name="aIndices"></param>
        /// <returns></returns>
        private CustomVertex.PositionTextured[] BuildPointsPlot(out int[] aIndices)
        {
            // Creating the array
            Spectrum tmpVect;

            int iCountVert = 0;
            for (int i = 0; i < m_PlotData.RetentionScansCount; i++)
            {
                tmpVect = (Spectrum)m_PlotData.PlotSamples[i];

                iCountVert += tmpVect.m_PD.Length;
            }

            //m_aMzCountForRT = new int[plotData.RetentionScansCount];

            // Create enough space for data the *2 is needed for lines the second half is for vertices without intensity
            CustomVertex.PositionTextured[] aVertices = new CustomVertex.PositionTextured[iCountVert * 2];
            aIndices = new int[iCountVert * 2];




            // helper info to check the max IZ in the scan
            Vector2[] aMinMaxMzInRow = new Vector2[m_PlotData.RetentionScansCount];
            //////////////////////////////////////////////////////////////////////////
            // Reading array and getting the extreme points 
            int iVerticiesCounter = 0;
            for (int i = 0; i < m_PlotData.RetentionScansCount; i++)
            {


                // read the retention time
                tmpVect = (Spectrum)m_PlotData.PlotSamples[i];
                //m_aMzCountForRT[i] = tmpVect.m_PD.Length;

                for (int j = 0; j < tmpVect.m_PD.Length; j++)
                {
                    // read the MZ and Intensity
                    aVertices[iVerticiesCounter] = new CustomVertex.PositionTextured(new Vector3(
                        tmpVect.m_RT,
                        tmpVect.m_PD[j].Intensity,
                        tmpVect.m_PD[j].MZ)
                        , 0, 0);

                    // Find min and max values of IZ
                    if (j == 0)
                    {
                        aMinMaxMzInRow[i] = new Vector2(aVertices[iVerticiesCounter].Z, aVertices[iVerticiesCounter].Z);
                    }
                    if (aMinMaxMzInRow[i].X > aVertices[iVerticiesCounter].Z)
                    {
                        aMinMaxMzInRow[i].X = aVertices[iVerticiesCounter].Z;
                    }
                    if (aMinMaxMzInRow[i].Y < aVertices[iVerticiesCounter].Z)
                    {
                        aMinMaxMzInRow[i].Y = aVertices[iVerticiesCounter].Z;
                    }


                    iVerticiesCounter++;
                }

            }


            Vector3 vPlotScale = new Vector3();
            vPlotScale.X = m_vPlotArea.X / (m_PlotData.MaxRetentionTime - m_PlotData.MinRetentionTime);
            vPlotScale.Z = m_vPlotArea.Z / (m_PlotData.MaxMZ - m_PlotData.MinMZ);
            vPlotScale.Y = m_vPlotArea.Y / m_PlotData.MaxIntensity;


            // setting the vertices color since we have a the max now
            int iIndexCounter = 0;
            //int iNextColorChange = 0;
            //int iRTCounter = 0;
            for (int i = 0; i < aVertices.Length / 2; i++)
            {

                float fScale = aVertices[i].Y / m_PlotData.MaxIntensity;
                //aVertices[i].Color = m_PlotData.PlotSettings.ColorGradient.GetColor(fScale).ToArgb();

                Vector3 VTranslate = new Vector3();
                // translate verticies so the start is at 0,0
                VTranslate = aVertices[i].Position - new Vector3(m_PlotData.MinRetentionTime, 0, m_PlotData.MinMZ);
                // rescale verts so it fits to the view
                VTranslate.X *= vPlotScale.X;
                VTranslate.Y *= vPlotScale.Y;
                VTranslate.Z *= -vPlotScale.Z;
                // move the plot so the point (RTMin, RTMin.MZ) is at the (40,-50 pos)
                VTranslate -= new Vector3(m_vPlotArea.X / 2, 0, -m_vPlotArea.Z / 2);
                aVertices[i] = new CustomVertex.PositionTextured(VTranslate, fScale, 0);
                aVertices[aVertices.Length / 2 + i] = new CustomVertex.PositionTextured(VTranslate, 0, 0);
                aVertices[aVertices.Length / 2 + i].Y = 0.0f;
                aIndices[iIndexCounter++] = i;
                aIndices[iIndexCounter++] = i + aVertices.Length / 2;

            }

            return aVertices;
        }


        /// <summary>
        /// Builds vertices and indices for lines plot
        /// </summary>
        /// <param name="aIndices"></param>
        /// <returns></returns>
        private CustomVertex.PositionColored[] BuildLinesPlot(out int[] aIndices)
        {
            ColorChanger cChng = new ColorChanger();
            // Creating the array
            Spectrum tmpVect;

            int iCountVert = 0;
            for (int i = 0; i < m_PlotData.RetentionScansCount; i++)
            {
                tmpVect = (Spectrum)m_PlotData.PlotSamples[i];
                if (tmpVect.NumberOfSamples <= 1) continue;
                iCountVert += tmpVect.m_PD.Length + 2;//border vertices too
            }
            CustomVertex.PositionColored[] aVerticesForLineType;

            Vector3 vPlotScale = new Vector3();
            vPlotScale.X = m_vPlotArea.X / (m_PlotData.MaxRetentionTime - m_PlotData.MinRetentionTime);
            vPlotScale.Z = m_vPlotArea.Z / (m_PlotData.MaxMZ - m_PlotData.MinMZ);
            vPlotScale.Y = m_vPlotArea.Y / m_PlotData.MaxIntensity;

            aVerticesForLineType = new CustomVertex.PositionColored[2 * (iCountVert)];

            //////////////////////////////////////////////////////////////////////////
            // Reading array 
            int iVerticiesCounter = 0;

            m_LinePlotData.Clear();
            for (int i = 0; i < m_PlotData.RetentionScansCount; i++)
            {
                // read the retention time
                tmpVect = (Spectrum)m_PlotData.PlotSamples[i];
                if (tmpVect.NumberOfSamples <= 1) continue;
                m_LinePlotData.Add(tmpVect.NumberOfSamples);
                //m_aMzCountForRT[i] = tmpVect.m_PD.Length;

                //left border vertex
                aVerticesForLineType[iVerticiesCounter++] = new CustomVertex.PositionColored(new Vector3(
                        tmpVect.m_RT,
                        0,
                        m_PlotData.MinMZ)
                        , cChng.GetColor().ToArgb());

                for (int j = 0; j < tmpVect.m_PD.Length; j++)
                {
                    // read the MZ and Intensity
                    aVerticesForLineType[iVerticiesCounter] = new CustomVertex.PositionColored(new Vector3(
                        tmpVect.m_RT,
                        tmpVect.m_PD[j].Intensity,
                        tmpVect.m_PD[j].MZ)
                        , cChng.GetColor().ToArgb());

                    iVerticiesCounter++;
                }

                //right border vertex
                aVerticesForLineType[iVerticiesCounter++] = new CustomVertex.PositionColored(new Vector3(
                        tmpVect.m_RT,
                        0,
                        m_PlotData.MaxMZ)
                        , cChng.GetColor().ToArgb());
                cChng.Next();
            }

            // setting the vertices color since we have a the max now            
            for (int i = 0; i < aVerticesForLineType.Length / 2; i++)
            {


                float fScale = aVerticesForLineType[i].Y / m_PlotData.MaxIntensity;
                //aVerticesForLineType[i].Color = m_PlotData.PlotSettings.ColorGradient.GetColor(fScale).ToArgb();

                Vector3 VTranslate = new Vector3();
                // translate verticies so the start is at 0,0
                VTranslate = aVerticesForLineType[i].Position - new Vector3(m_PlotData.MinRetentionTime, 0, m_PlotData.MinMZ);
                // rescale verts so it fits to the view
                VTranslate.X *= vPlotScale.X;
                VTranslate.Y *= vPlotScale.Y;
                VTranslate.Z *= -vPlotScale.Z;
                // move the plot so the point (RTMin, RTMin.MZ) is at the (40,-50 pos)
                VTranslate -= new Vector3(m_vPlotArea.X / 2, 0, -m_vPlotArea.Z / 2);
                aVerticesForLineType[i].Position = VTranslate;
                aVerticesForLineType[aVerticesForLineType.Length / 2 + i] = new CustomVertex.PositionColored(VTranslate, aVerticesForLineType[i].Color);
                aVerticesForLineType[aVerticesForLineType.Length / 2 + i].Y = 0.0f;

            }

            aIndices = new int[aVerticesForLineType.Length];
            int indexCount = 0;
            //aIndices[indexCount++] = aVerticesForLineType.Length / 2 + indexLineStart;
            //aIndices[indexCount++] = indexLineStart;
            //aIndices[indexCount++] = aVerticesForLineType.Length / 2 + indexLineStart + 1;
            for (int row = 0; row < aVerticesForLineType.Length / 2; row++)
            {
                aIndices[indexCount++] = aVerticesForLineType.Length / 2 + row;
                aIndices[indexCount++] = row;
            }

            return aVerticesForLineType;
        }

        /// <summary>
        /// Builds vertices and indices for surface plot
        /// </summary>
        /// <param name="aIndices"></param>
        /// <returns></returns>
        private CustomVertex.PositionNormalTextured[] BuildSurfacePlot(out int[] aIndices)
        {

            //////////////////////////////////////////////////////////////////////////
            //right now only using uniform grid, in the future also from actual data
            m_iMzGrid = m_PlotData.PlotSettings.PlotSubsetDescription.m_iResolutionMZ;
            CustomVertex.PositionNormalTextured[] aVertices = BuildUniformGrid(0, m_iMzGrid, out aIndices);


            //scale and center the plot
            Vector3 vPlotScale;
            vPlotScale.X = m_vPlotArea.X / (m_PlotData.MaxRetentionTime - m_PlotData.MinRetentionTime);
            vPlotScale.Z = m_vPlotArea.Z / (m_PlotData.MaxMZ - m_PlotData.MinMZ);
            vPlotScale.Y = m_vPlotArea.Y / m_PlotData.MaxIntensity;

            for (int i = 0; i < aVertices.Length; i++)
            {
                Vector3 pos = aVertices[i].Position;
                pos -= new Vector3(m_PlotData.MinRetentionTime, 0, m_PlotData.MinMZ);
                pos.X *= vPlotScale.X;
                pos.Y *= vPlotScale.Y;
                pos.Z *= -vPlotScale.Z;
                pos -= new Vector3(m_vPlotArea.X / 2, 0, -m_vPlotArea.Z / 2);
                aVertices[i].Position = pos;
            }

            // calculate normals after all transformations
            for (int i = 0; i < aVertices.Length; i++)
                aVertices[i].Normal = new Vector3(0, 0, 0);

            for (int i = 0; i < aIndices.Length / 3; i++)
            {
                int index1 = aIndices[i * 3];
                int index2 = aIndices[i * 3 + 1];
                int index3 = aIndices[i * 3 + 2];

                Vector3 side1 = aVertices[index1].Position - aVertices[index3].Position;
                Vector3 side2 = aVertices[index1].Position - aVertices[index2].Position;
                Vector3 normal = Vector3.Cross(side2, side1);


                aVertices[index1].Normal += normal;
                aVertices[index2].Normal += normal;
                aVertices[index3].Normal += normal;
            }

            for (int i = 0; i < aVertices.Length; i++)
            {
                Vector3 v = aVertices[i].Normal;
                v.Normalize();
                aVertices[i].Normal = v;
            }


            return aVertices;
        }



        #endregion

        #region Data selection methods
        private bool SelectVertex(Vector3 rayFrom, Vector3 rayDir, float mzSnapRange, float rtSnapRange, float intSnapRange, out Vector3 selectedPos, out DataPoint selectedData, out float selectedRT)
        {
            float snapRadius = (float)Math.Sqrt(mzSnapRange * mzSnapRange + rtSnapRange * rtSnapRange + intSnapRange * intSnapRange);
            Vector3 vPlotScale;
            vPlotScale.X = m_vPlotArea.X / (m_PlotData.MaxRetentionTime - m_PlotData.MinRetentionTime);
            vPlotScale.Z = m_vPlotArea.Z / (m_PlotData.MaxMZ - m_PlotData.MinMZ);
            vPlotScale.Y = m_vPlotArea.Y / m_PlotData.MaxIntensity;



            rayFrom += new Vector3(m_vPlotArea.X / 2, 0, -m_vPlotArea.Z / 2);
            rayFrom.X /= vPlotScale.X;
            rayFrom.Y /= vPlotScale.Y;
            rayFrom.Z /= -vPlotScale.Z;
            rayFrom += new Vector3(m_PlotData.MinRetentionTime, 0, m_PlotData.MinMZ);

            rayDir.X /= vPlotScale.X;
            rayDir.Y /= vPlotScale.Y;
            rayDir.Z /= -vPlotScale.Z;

            selectedPos = new Vector3();
            selectedData = new DataPoint();
            selectedRT = 0;

            float fClosestPoint = -1;
            float fHighestPoint = 0;
            int closestRT = 0;
            int closestMZ = 0;

            for (int rt = 0; rt < m_PlotData.PlotSamples.Length; rt++)
            {
                for (int mz = 0; mz < m_PlotData.PlotSamples[rt].NumberOfSamples; mz++)
                {
                    if (m_PlotData.PlotSamples[rt].m_PD[mz].Intensity < m_fSelectionIntensityTreshold * m_PlotData.MaxIntensity) continue;
                    //compute distance to line
                    Vector3 pos = new Vector3(m_PlotData.PlotSamples[rt].m_RT, m_PlotData.PlotSamples[rt].m_PD[mz].Intensity, m_PlotData.PlotSamples[rt].m_PD[mz].MZ);
                    float t;
                    float dist = CalculateRayPointDistance(rayFrom, rayDir, pos, out t);

                    if (dist < snapRadius)
                    {
                        //check if it's close enough
                        Vector3 closestPoint = rayFrom + rayDir * t;
                        Vector3 vDist = pos - closestPoint;
                        if (Math.Abs(vDist.X) <= rtSnapRange && Math.Abs(vDist.Y) <= intSnapRange && Math.Abs(vDist.Z) <= mzSnapRange)
                        {
                            if (pos.Y > fHighestPoint || fHighestPoint == 0)
                            {
                                fHighestPoint = pos.Y;
                                closestRT = rt;
                                closestMZ = mz;
                            }
                        }

                    }
                }
            }

            if (fClosestPoint != -1 || fHighestPoint != 0)
            {
                selectedData = m_PlotData.PlotSamples[closestRT].m_PD[closestMZ];
                selectedRT = m_PlotData.PlotSamples[closestRT].m_RT;

                selectedPos = new Vector3(selectedRT, selectedData.Intensity, selectedData.MZ);
                selectedPos -= new Vector3(m_PlotData.MinRetentionTime, 0, m_PlotData.MinMZ);
                selectedPos.X *= vPlotScale.X;
                selectedPos.Y *= vPlotScale.Y;
                selectedPos.Z *= -vPlotScale.Z;
                selectedPos -= new Vector3(m_vPlotArea.X / 2, 0, -m_vPlotArea.Z / 2);


                return true;

            }
            return false;
        }
        #endregion

        #region Device Callbacks

        /// <summary>
        /// Called after a new device is created(only once), things that are in the managed pool should be created here.
        /// </summary>
        /// <param name="obj">Not used</param>
        /// <param name="args">Not used</param>
        protected override void OnCreateDevice(object obj, EventArgs args)
        {
            base.OnCreateDevice(obj, args);
            ResetCamera();

            if (m_bUseShaders)
            {
                System.IO.Stream imgStream = null;

                // get a reference to the current assembly
                Assembly a = Assembly.GetExecutingAssembly();

                // get a list of resource names from the manifest
                string[] resNames = a.GetManifestResourceNames();


                foreach (string s in resNames)
                {
                    if (s.EndsWith(".fx"))
                    {
                        // attach to stream to the resource in the manifest
                        imgStream = a.GetManifestResourceStream(s);
                        break;
                    }
                }
                String comErr = "";
                //m_effectSurface = Effect.FromFile(d3dDevice, @"C:\Users\gloVA\Desktop\Plot\plottingtheplot\Plot3DControl\simple.fx", null, ShaderFlags.None, null, out comErr);                
                m_effectSurface = Effect.FromStream(d3dDevice, imgStream, null, ShaderFlags.None, null, out comErr);
            }

            m_debugFont = new Microsoft.DirectX.Direct3D.Font(d3dDevice, 15, 0, FontWeight.Normal, 1, false, CharacterSet.Default, Precision.Default,
                FontQuality.Default, PitchAndFamily.DefaultPitch | PitchAndFamily.FamilyDoNotCare, "Arial");
            m_axisFont = new Microsoft.DirectX.Direct3D.Font(d3dDevice, 15, 0, FontWeight.Bold, 1, false, CharacterSet.Default, Precision.Default,
                FontQuality.Default, PitchAndFamily.DefaultPitch | PitchAndFamily.FamilyDoNotCare, "Arial");
            m_LoadingFont = new Microsoft.DirectX.Direct3D.Font(d3dDevice, 30, 0, FontWeight.Bold, 1, false, CharacterSet.Default, Precision.Default,
                FontQuality.Default, PitchAndFamily.DefaultPitch | PitchAndFamily.FamilyDoNotCare, "Arial");
            m_PlotTitleFont = new Microsoft.DirectX.Direct3D.Font(d3dDevice, 30, 0, FontWeight.Bold, 1, false, CharacterSet.Default, Precision.Default,
                FontQuality.Default, PitchAndFamily.DefaultPitch | PitchAndFamily.FamilyDoNotCare, "Arial");
            m_spriteObject = new Sprite(d3dDevice);
            m_mPoint = Mesh.Sphere(d3dDevice, 0.5f, 4, 4);
            this.m_mMaterial = new Material();



            m_SurfaceMaterial = new Material();
            m_SurfaceMaterial.Ambient = Color.DarkGray;
            m_SurfaceMaterial.Diffuse = Color.White;
            //////////////////////////////////////////////////////////////////////////
            //Load axis meshes
            float fAxisThickness = 0.4f;
            m_mAxisArrow = Mesh.Cylinder(d3dDevice, fAxisThickness * 3, 0, 10 * fAxisThickness, 10, 1);
            m_mAxisCylinder = Mesh.Cylinder(d3dDevice, fAxisThickness, fAxisThickness, 1, 10, 1);
            m_mOrigin = Mesh.Sphere(d3dDevice, 2f * fAxisThickness, 10, 10);

            //create plot bounding box 
            m_ibPlotAreaBBTriangles = new IndexBuffer(typeof(int), 36, d3dDevice, Usage.WriteOnly, Pool.Managed);
            m_ibPlotAreaBBLines = new IndexBuffer(typeof(int), 24, d3dDevice, Usage.WriteOnly, Pool.Managed);

            m_vbPlotArea = new VertexBuffer(typeof(CustomVertex.PositionColored), 8, d3dDevice, Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Managed);

            CustomVertex.PositionColored[] vbData = (CustomVertex.PositionColored[])m_vbPlotArea.Lock(0, 0);

            int color = Color.FromArgb(50, 80, 80, 80).ToArgb();

            vbData[0] = new CustomVertex.PositionColored(-1, -1, -1, color);
            vbData[1] = new CustomVertex.PositionColored(1, -1, -1, color);
            vbData[2] = new CustomVertex.PositionColored(1, -1, 1, color);
            vbData[3] = new CustomVertex.PositionColored(-1, -1, 1, color);
            vbData[4] = new CustomVertex.PositionColored(-1, 1, -1, color);
            vbData[5] = new CustomVertex.PositionColored(1, 1, -1, color);
            vbData[6] = new CustomVertex.PositionColored(1, 1, 1, color);
            vbData[7] = new CustomVertex.PositionColored(-1, 1, 1, color);
            m_vbPlotArea.Unlock();

            //selection cube
            m_vbSelection = new VertexBuffer(typeof(CustomVertex.PositionColored), 8, d3dDevice, Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Managed);

            vbData = (CustomVertex.PositionColored[])m_vbSelection.Lock(0, 0);

            color = Color.FromArgb(50, 80, 0, 0).ToArgb();

            vbData[0] = new CustomVertex.PositionColored(-1, -1, -1, color);
            vbData[1] = new CustomVertex.PositionColored(1, -1, -1, color);
            vbData[2] = new CustomVertex.PositionColored(1, -1, 1, color);
            vbData[3] = new CustomVertex.PositionColored(-1, -1, 1, color);
            vbData[4] = new CustomVertex.PositionColored(-1, 1, -1, color);
            vbData[5] = new CustomVertex.PositionColored(1, 1, -1, color);
            vbData[6] = new CustomVertex.PositionColored(1, 1, 1, color);
            vbData[7] = new CustomVertex.PositionColored(-1, 1, 1, color);
            m_vbSelection.Unlock();

            int[] triangleIndices ={0,2,1,0,3,2,
                              0,4,3,4,7,3,
                              1,6,5,1,2,6,
                              0,5,4,0,1,5,
                              3,7,6,3,6,2,
                              4,5,6,4,6,7};

            m_ibPlotAreaBBTriangles.SetData(triangleIndices, 0, LockFlags.None);

            int[] lineIndices = { 0, 1, 1, 2, 2, 3, 3, 0, 0, 4, 1, 5, 2, 6, 3, 7, 4, 5, 5, 6, 6, 7, 7, 4 };

            m_ibPlotAreaBBLines.SetData(lineIndices, 0, LockFlags.None);

            //m_tGrayBox = TextureLoader.FromFile(d3dDevice, "Gray.png", 0, 0, 0, Usage.None, Format.A8R8G8B8, Pool.Managed, Filter.Linear, Filter.Linear, 0);



            m_mVertexSelectionMesh = Mesh.Sphere(d3dDevice, 2, 10, 10);


            BuildAxisLabels();


            BuildDashedLine();



        }

        /// <summary>
        /// Called after device reset(new present parameters) and just after creating the device and calling OnCreateDevice
        /// Everything that isn't in the managed pool should be created here.
        /// </summary>
        /// <param name="obj">Not used</param>
        /// <param name="args">Not used</param>
        protected override void OnResetDevice(object obj, EventArgs args)
        {
            base.OnResetDevice(obj, args);
            SetProjection();
            SetCamera();

            m_tGrayBox = new Texture(d3dDevice, 1, 1, 0, Usage.Dynamic, Format.A8R8G8B8, Pool.Default);
            GraphicsStream stream = m_tGrayBox.LockRectangle(0, LockFlags.Discard);
            stream.WriteByte(50);
            stream.WriteByte(50);
            stream.WriteByte(50);
            stream.WriteByte(50);

            m_tGrayBox.UnlockRectangle(0);

            d3dDevice.Lights[0].Type = LightType.Directional;
            d3dDevice.Lights[0].Diffuse = Color.White;
            d3dDevice.Lights[0].Ambient = Color.DarkGray;
            d3dDevice.Lights[0].Direction = new Vector3(0f, -0.3f, -1f);
            d3dDevice.Lights[0].Enabled = true;

        }
        /// <summary>
        /// Called after the device has been lost
        /// </summary>
        /// <param name="obj">Not used</param>
        /// <param name="args">Not used</param>
        protected override void OnLostDevice(object obj, EventArgs args)
        {
            base.OnLostDevice(obj, args);
            m_tGrayBox.Dispose();
        }

        /// <summary>
        /// Called when the application is exiting and the device is being destroyed. Delete all device objects here
        /// (meshes, vertex buffers etc.)
        /// </summary>
        /// <param name="obj">Not used</param>
        /// <param name="args">Not used</param>
        protected override void OnDestroyDevice(object obj, EventArgs args)
        {
            base.OnDestroyDevice(obj, args);
            //fonts
            m_debugFont.Dispose();
            m_axisFont.Dispose();
            m_LoadingFont.Dispose();
            m_PlotTitleFont.Dispose();
            m_spriteObject.Dispose();
            m_mPoint.Dispose();

            //textures
            if (m_WhiteTexture != null)
                m_WhiteTexture.Dispose();
            if (m_GradientTexture != null)
                m_GradientTexture.Dispose();
            if (m_DashedLineTexture != null)
                m_DashedLineTexture.Dispose();
            //vertex/index buffers
            if (m_vbPlotVertices != null)
                m_vbPlotVertices.Dispose();
            if (m_ibPlotIndices != null)
                m_ibPlotIndices.Dispose();

            //meshes
            if (m_MZLabelMesh != null)
                m_MZLabelMesh.Dispose();
            if (m_RTLabelMesh != null)
                m_MZLabelMesh.Dispose();
            if (m_IntensityLabelMesh != null)
                m_IntensityLabelMesh.Dispose();

            if (m_mVertexSelectionMesh != null)
                m_mVertexSelectionMesh.Dispose();
        }
        #endregion



        #region Camera Related


        /// <summary>
        /// Resets the camera to default parameters(position, zoom)
        /// </summary>
        public void ResetCamera()
        {
            m_vInitialCameraPos = new Vector3(-10, m_vPlotArea.Y / 2, 2 * m_vPlotArea.Z);
            m_vInitialLookAt = new Vector3(-10, m_vPlotArea.Y / 4, 0);
            m_vCameraPos = m_vInitialCameraPos;
            m_vLookAt = m_vInitialLookAt;
            m_fCameraRotationX = 0;
            m_fCameraRotationY = 0;
            m_PlotZoom.m_fMZOffset = m_PlotZoom.m_fRTOffset = 0;
            m_PlotZoom.m_fMZSubset = m_PlotZoom.m_fRTSubset = 1;
            SetCamera();
            Refresh();
        }

        /// <summary>
        /// Sets correct projection- currenlty perspective only
        /// </summary>
        void SetProjection()
        {
            if (d3dDevice == null) return;

            if (m_PlotData != null && m_PlotData.PlotSettings.ViewSettings.ProjectionType == ProjectionType.Orthographic)
            {
                float fAspect = (float)currentPresentParameters.BackBufferWidth / (float)currentPresentParameters.BackBufferHeight;
                float fDesiredAspect = 1.3f;
                if (fAspect < 1.3f)
                {
                    float fOrthoSize = 2 * m_fCurrentCameraDistance * (float)Math.Tan(Math.PI / 8);
                    m_ProjectionMatrix = Matrix.OrthoRH(fOrthoSize, fOrthoSize / fAspect, 0.1f, 1000f);
                }
                else
                {
                    float fOrthoSize = 2 * m_fCurrentCameraDistance * (float)Math.Tan(Math.PI / 8);
                    m_ProjectionMatrix = Matrix.OrthoRH(fAspect * fOrthoSize / fDesiredAspect, fOrthoSize / fDesiredAspect, 0.1f, 1000f);
                }
            }
            else
            {
                m_ProjectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 4, (float)currentPresentParameters.BackBufferWidth / (float)currentPresentParameters.BackBufferHeight, 0.5f, 500.0f);
            }
            d3dDevice.Transform.Projection = m_ProjectionMatrix;

            d3dDevice.Transform.World = Matrix.Identity;
            //d3dDevice.Transform.View = Matrix.LookAtRH(new Vector3(0, 0, 200), new Vector3(0, 0, 0), new Vector3(0, 1, 0));

            d3dDevice.Transform.View = Matrix.LookAtRH(m_vCameraPos, m_vLookAt, new Vector3(0, 1, 0));
        }

        /// <summary>
        /// Builds and sets a view matrix based on current camera settings
        /// </summary>
        void SetCamera()
        {
            if (d3dDevice == null) return;
            m_vCameraPos = m_vInitialCameraPos - m_vInitialLookAt;

            m_vCameraPos.TransformNormal(Matrix.RotationX(m_fCameraRotationX));
            m_vCameraPos.TransformNormal(Matrix.RotationY(m_fCameraRotationY));
            m_vCameraPos *= m_fCurrentCameraDistance / m_vCameraPos.Length();
            m_vCameraPos += m_vInitialLookAt;
            m_ViewMatrix = Matrix.LookAtRH(m_vCameraPos, m_vLookAt, new Vector3(0, 1, 0));



            d3dDevice.Transform.View = m_ViewMatrix;
        }


        #endregion


        #region Rendering methods

        private void SetColorMode(bool printerFriendly)
        {
            if (printerFriendly)
            {
                m_ColorBgCurrent = m_ColorBgPrinter;
                m_ColorFontCurrent = m_ColorFontPriner;
                m_ColorAxisCurrent = m_ColorAxisPrinter;
                m_bRenderBoundingBox = false;
            }
            else
            {
                m_ColorBgCurrent = m_ColorBgNormal;
                m_ColorFontCurrent = m_ColorFontNormal;
                m_ColorAxisCurrent = m_ColorAxisNormal;
                m_bRenderBoundingBox = true;
            }
        }
        /// <summary>
        /// Renders the plot to a bitmap
        /// </summary>
        /// <param name="bitmapWidth">Bitmap width in pixels</param>
        /// <param name="bitmapHeight">Bitmap height in pixels</param>
        /// <param name="printerColors">If this is true then a white background is used with black text</param>
        /// <returns>Bitmap containing the plot</returns>
        public Bitmap RenderToBitmap(int bitmapWidth, int bitmapHeight, bool printerColors)
        {
            if (d3dDevice == null) return null;
            try
            {
                //if (printerColors)
                //{
                //    m_ColorBgCurrent = m_ColorBgPrinter;
                //    m_ColorFontCurrent = m_ColorFontPriner;
                //    m_ColorAxisCurrent = m_ColorAxisPrinter;
                //    m_bRenderBoundingBox = false;
                //}
                bool oldPrinterColors = m_PlotData.PlotSettings.ViewSettings.PrinterColors;
                SetColorMode(printerColors);
                RenderToSurface rtsHelper = new RenderToSurface(d3dDevice, bitmapWidth, bitmapHeight, Format.A8R8G8B8, true, DepthFormat.D16);
                Texture renderTexture = new Texture(d3dDevice, bitmapWidth, bitmapHeight, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default);
                Surface renderSurface = renderTexture.GetSurfaceLevel(0);
                Surface old = d3dDevice.GetRenderTarget(0);
                //////////////////////////////////////////////////////////////////////////
                //not implemented
                rtsHelper.BeginScene(renderSurface);
                RenderControl();
                rtsHelper.EndScene(Filter.None);

                SetColorMode(oldPrinterColors);
                GraphicsStream img_stream = TextureLoader.SaveToStream(ImageFileFormat.Png, renderTexture);
                Bitmap bmp = new Bitmap(Bitmap.FromStream(img_stream));
                //bmp.Save("o.png");
                return bmp;
            }
            catch (System.Exception e)
            {
                m_ColorBgCurrent = m_ColorBgNormal;
                MessageBox.Show(e.Message);
            }
            return null;

        }
        /// <summary>
        /// Renders control
        /// </summary>
        protected override void RenderControl()
        {
            if (d3dDevice == null) return;

            m_iFrameNumber++;

            d3dDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, m_ColorBgCurrent, 1, 0);
            if (ErrorTextPresent)
            {
                RenderErrorText();
                return;
            }

            if (m_PlotData != null && m_PlotData.PlotSettings.ViewSettings.PlotTitle != "" && m_PlotData.PlotSettings.ViewSettings.PlotTitle != null)
            {
                m_PlotTitleFont.DrawText(null, m_PlotData.PlotSettings.ViewSettings.PlotTitle, new Point(
                    currentPresentParameters.BackBufferWidth / 2 - 6 * m_PlotData.PlotSettings.ViewSettings.PlotTitle.Length, 20), m_ColorFontCurrent);
            }

            //RenderLineAxes();
            RenderAxisMarkers();
            d3dDevice.Clear(ClearFlags.ZBuffer, Color.Black, 1, 0);

            RenderSolidAxisNames();
            RenderIntensityMarkers();
            RenderGrid();
            RenderSolidAxes();
            RenderPlot();


            RenderSelectionBalls();


            if (m_bSelectionMode)
            {
                RenderSelection();
            }
            RenderBoundingBox();
            d3dDevice.Clear(ClearFlags.ZBuffer, Color.Black, 1, 0);

            if (m_eCurrentPlotType != PlotType.Lines)
            {
                RenderLegend();
            }


            RenderDebugText();

            if (m_bIsLoading)
                RenderLoadingScreen();

        }

        /// <summary>
        /// Renders all 3 axes using simple lines
        /// </summary>
        void RenderLineAxes()
        {
            d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
            d3dDevice.Transform.World = Matrix.Identity;
            //axis color-black
            Color black = Color.Black;

            CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[6];

            vertices[0].Color = Color.Black.ToArgb();
            vertices[0].Position = new Vector3(m_vPlotPosition.X - m_vPlotArea.X / 2, m_vPlotPosition.Y, m_vPlotPosition.Z + m_vPlotArea.Z / 2);

            vertices[1].Color = Color.Black.ToArgb();
            vertices[1].Position = new Vector3(m_vPlotPosition.X + m_vPlotArea.X / 2, m_vPlotPosition.Y, m_vPlotPosition.Z + m_vPlotArea.Z / 2);

            vertices[2].Color = Color.Black.ToArgb();
            vertices[2].Position = new Vector3(m_vPlotPosition.X - m_vPlotArea.X / 2, m_vPlotPosition.Y, m_vPlotPosition.Z + m_vPlotArea.Z / 2);

            vertices[3].Color = Color.Black.ToArgb();
            vertices[3].Position = new Vector3(m_vPlotPosition.X - m_vPlotArea.X / 2, m_vPlotPosition.Y, m_vPlotPosition.Z - m_vPlotArea.Z / 2);

            vertices[4].Color = Color.Black.ToArgb();
            vertices[4].Position = new Vector3(m_vPlotPosition.X - m_vPlotArea.X / 2, m_vPlotPosition.Y, m_vPlotPosition.Z + m_vPlotArea.Z / 2);

            vertices[5].Color = Color.Black.ToArgb();
            vertices[5].Position = new Vector3(m_vPlotPosition.X - m_vPlotArea.X / 2, m_vPlotPosition.Y + m_vPlotArea.Y, m_vPlotPosition.Z + m_vPlotArea.Z / 2);




            d3dDevice.DrawUserPrimitives(PrimitiveType.LineList, 3, vertices);
        }

        /// <summary>
        /// Renders all 3 axes using 3D graphics
        /// </summary>
        void RenderSolidAxes()
        {
            d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
            d3dDevice.Transform.World = Matrix.Identity;
            //axis color-black
            Color black = Color.Black;
            d3dDevice.RenderState.Lighting = false;


            float fAxisOffset = 0.3f;

            float fArrowOffset = 8.9f;

            d3dDevice.RenderState.Lighting = true;
            Material mat = new Material();
            mat.Ambient = m_ColorAxisCurrent;
            d3dDevice.Material = mat;
            Vector3 vbbPlotArea = m_vPlotArea * m_bbAreaScale;

            Vector3 vAxisOffset = new Vector3(-fAxisOffset, -fAxisOffset, fAxisOffset);
            //origin
            Matrix mOrigin = Matrix.Translation(m_vPlotPosition - (new Vector3(vbbPlotArea.X, 0, -vbbPlotArea.Z) * 0.5f) + vAxisOffset);
            d3dDevice.Transform.World = mOrigin;

            m_mOrigin.DrawSubset(0);

            //x axis
            d3dDevice.Transform.World = Matrix.Scaling(1, 1, vbbPlotArea.X + 6) * Matrix.RotationY(-(float)Math.PI / 2) * Matrix.Translation(m_vPlotPosition + new Vector3(7, 0, vbbPlotArea.Z) * 0.5f + vAxisOffset);
            m_mAxisCylinder.DrawSubset(0);

            d3dDevice.Transform.World = Matrix.RotationY((float)Math.PI / 2) * Matrix.Translation(m_vPlotPosition + new Vector3(vbbPlotArea.X + 1.5f * fArrowOffset, 0, vbbPlotArea.Z) * 0.5f + vAxisOffset);
            m_mAxisArrow.DrawSubset(0);

            //y axis
            d3dDevice.Transform.World = Matrix.Scaling(1, 1, vbbPlotArea.Y + 6) * Matrix.RotationX(-(float)Math.PI / 2) * Matrix.Translation(m_vPlotPosition + new Vector3(-vbbPlotArea.X, vbbPlotArea.Y + 7, vbbPlotArea.Z) * 0.5f + vAxisOffset);
            m_mAxisCylinder.DrawSubset(0);

            d3dDevice.Transform.World = Matrix.RotationX(-(float)Math.PI / 2) * Matrix.Translation(m_vPlotPosition + new Vector3(-vbbPlotArea.X, 2 * vbbPlotArea.Y + 1.5f * fArrowOffset, vbbPlotArea.Z) * 0.5f + vAxisOffset);
            m_mAxisArrow.DrawSubset(0);

            //z axis
            d3dDevice.Transform.World = Matrix.Scaling(1, 1, vbbPlotArea.Z + 6) * Matrix.Translation(m_vPlotPosition + new Vector3(-vbbPlotArea.X, 0, -7) * 0.5f + vAxisOffset);
            m_mAxisCylinder.DrawSubset(0);

            d3dDevice.Transform.World = Matrix.RotationY((float)Math.PI) * Matrix.Translation(m_vPlotPosition + new Vector3(-vbbPlotArea.X, 0, -vbbPlotArea.Z - 1.5f * fArrowOffset) * 0.5f + vAxisOffset);
            m_mAxisArrow.DrawSubset(0);

        }

        /// <summary>
        /// Renders axis labels using 3D text
        /// </summary>
        void RenderSolidAxisNames()
        {
            d3dDevice.Transform.World = Matrix.Identity;
            //axis color-black
            Color black = Color.Black;
            d3dDevice.RenderState.Lighting = false;


            float fAxisOffset = 0.3f;


            d3dDevice.RenderState.Lighting = true;
            Material mat = new Material();
            mat.Ambient = m_ColorAxisCurrent;
            d3dDevice.Material = mat;

            Vector3 vAxisOffset = new Vector3(-fAxisOffset, -fAxisOffset, fAxisOffset);
            //origin
            Matrix mOrigin = Matrix.Translation(m_vPlotPosition - (new Vector3(m_vPlotArea.X, 0, -m_vPlotArea.Z) * 0.5f) + vAxisOffset);


            //x axis


            d3dDevice.Transform.World = Matrix.Scaling(8.5f, 8.5f, 8.5f) * Matrix.RotationX(-0.9f) * Matrix.Translation(m_vPlotPosition + new Vector3(-55, -18, m_vPlotArea.Z + 46) * 0.5f);
            m_RTLabelMesh.DrawSubset(0);

            ////y axis
            if (m_fCameraRotationY > 1.75 * Math.PI || m_fCameraRotationY < 0.75 * Math.PI)
            {
                d3dDevice.Transform.World = Matrix.Scaling(8.5f, 8.5f, 8.5f) * Matrix.RotationZ(1.57079f) * Matrix.RotationY(0.75f) * Matrix.Translation(m_vPlotPosition + new Vector3(-35 - m_vPlotArea.X, m_vPlotArea.Y - 34, m_vPlotArea.Z + 35) * 0.5f);
                m_IntensityLabelMesh.DrawSubset(0);//
            }
            else
            {
                d3dDevice.Transform.World = Matrix.Scaling(8.5f, 8.5f, 8.5f) * Matrix.RotationZ(1.57079f) * Matrix.RotationY((float)Math.PI + 0.75f) * Matrix.Translation(m_vPlotPosition + new Vector3(-35 - m_vPlotArea.X, m_vPlotArea.Y - 34, m_vPlotArea.Z + 35) * 0.5f);
                m_IntensityLabelMesh.DrawSubset(0);//
            }


            //z axis
            d3dDevice.Transform.World = Matrix.Scaling(8.5f, 8.5f, 8.5f) * Matrix.RotationY(-1.57079f) * Matrix.RotationZ(-0.9f) * Matrix.Translation(m_vPlotPosition + new Vector3(-46 - m_vPlotArea.X, -18, -15) * 0.5f);
            m_MZLabelMesh.DrawSubset(0);



        }

        /// <summary>
        /// Renders a dashed line between 2 points
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        void RenderDashedLine(Vector3 from, Vector3 to)
        {
            CustomVertex.PositionTextured[] vertices = new CustomVertex.PositionTextured[2];
            vertices[0] = new CustomVertex.PositionTextured(from, 0, 0);
            vertices[1] = new CustomVertex.PositionTextured(to, (int)((from - to).Length() / 5.0f) + 0.5f, 0);

            d3dDevice.VertexFormat = CustomVertex.PositionTextured.Format;
            d3dDevice.SetTexture(0, m_DashedLineTexture);
            d3dDevice.DrawUserPrimitives(PrimitiveType.LineList, 1, vertices);
            d3dDevice.SetTexture(0, null);
        }

        /// <summary>
        /// Renders intensity markers and text labels
        /// </summary>
        void RenderIntensityMarkers()
        {
            if (m_PlotData == null) return;
            if (m_vbPlotVertices == null) return;

            d3dDevice.Transform.World = Matrix.Identity;
            d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
            d3dDevice.DrawUserPrimitives(PrimitiveType.LineList, m_NumIntensityMarkers, m_IntensityMarkerPoints);

            for (int i = 0; i < m_NumIntensityMarkers; i++)
            {
                Vector2 vPos = WorldToScreen(m_IntensityMarkers[i].m_vPosition);
                m_axisFont.DrawText(null, m_IntensityMarkers[i].m_fValue.ToString("F01"), new Point((int)vPos.X, (int)vPos.Y), m_ColorFontCurrent);
            }
        }

        /// <summary>
        /// Renders MZ and RT markers and text labels
        /// </summary>
        void RenderAxisMarkers()
        {
            if (m_PlotData == null) return;
            if (m_vbPlotVertices == null) return;
            d3dDevice.RenderState.Lighting = false;
            d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
            d3dDevice.Transform.World = Matrix.Identity;
            //axis color-black




            int[] aDivisionSteps = new int[] { 1, 2, 5, 10, 25, 50, 100, 200 };
            int orderOfmagnitude = (int)Math.Log10(AxisMaxRT - AxisMinRT) - 2;
            float multiplier = (float)Math.Pow(10, orderOfmagnitude);

            int currentStep = aDivisionSteps.Length - 1;
            while (currentStep >= 0 && (AxisMaxRT - AxisMinRT) / (multiplier * aDivisionSteps[currentStep]) < 4)
            {
                currentStep--;
            }

            float rtLabelWidth = 8 * ((int)Math.Log10(AxisMaxRT) + 1);
            float labelHeight = 10;
            float rtXOffset = -rtLabelWidth / 2.0f * (1 + (float)Math.Sin(m_fCameraRotationY));
            float rtYOffset = -labelHeight / 2.0f * (float)Math.Sin(m_fCameraRotationY);

            //rt axis
            m_RTMarkerPositions.Clear();
            CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[2];
            float rt = AxisMinRT;
            rt = GetRoundNumber(rt, aDivisionSteps[currentStep] * multiplier);
            for (; rt < AxisMaxRT; rt = GetNewRoundNumber(rt, aDivisionSteps[currentStep] * multiplier))
            {
                float pos = (rt - AxisMinRT) / (m_PlotZoom.m_fRTSubset * m_PlotData.RTRange);
                Vector3 markerPos = m_vPlotPosition - new Vector3(m_vPlotArea.X, 0, -m_vPlotArea.Z) * (0.5f + m_bbAreaScale - 1.0f);
                markerPos.X += pos * m_vPlotArea.X;
                m_RTMarkerPositions.Add(markerPos);//save marker position

                Vector3 secondPoint = markerPos + new Vector3(0, -1.5f, 4);
                vertices[0].Position = markerPos;
                vertices[0].Color = Color.Black.ToArgb();

                vertices[1].Position = secondPoint;
                vertices[1].Color = Color.Black.ToArgb();

                d3dDevice.DrawUserPrimitives(PrimitiveType.LineList, 1, vertices);

                secondPoint.Z += 2;

                Vector2 vPos = WorldToScreen(secondPoint);

                m_axisFont.DrawText(null, rt.ToString("F01"), new Point((int)(vPos.X + rtXOffset), (int)(vPos.Y + rtYOffset)), m_ColorFontCurrent);

            }

            //mz axis
            m_MZMarkerPositions.Clear();
            orderOfmagnitude = (int)Math.Log10(AxisMaxMZ - AxisMinMZ) - 2;
            multiplier = (float)Math.Pow(10, orderOfmagnitude);

            currentStep = aDivisionSteps.Length - 1;
            while (currentStep >= 0 && (AxisMaxMZ - AxisMinMZ) / (multiplier * aDivisionSteps[currentStep]) < 4)
            {
                currentStep--;
            }
            float mzLabelWidth = 8 * ((int)Math.Log10(AxisMaxMZ) + 1);
            //float labelHeight = 10;
            float mzXOffset = -mzLabelWidth / 2.0f * (1 + (float)Math.Cos(m_fCameraRotationY));
            float mzYOffset = -labelHeight / 2.0f * (float)Math.Cos(m_fCameraRotationY);
            //float mzDivider=


            float mz = AxisMinMZ;
            mz = GetRoundNumber(mz, multiplier * aDivisionSteps[currentStep]);
            for (; mz < AxisMaxMZ; mz = GetNewRoundNumber(mz, multiplier * aDivisionSteps[currentStep]))
            {
                float pos = (mz - AxisMinMZ) / (m_PlotZoom.m_fMZSubset * m_PlotData.MZRange);
                Vector3 markerPos = m_vPlotPosition - new Vector3(m_vPlotArea.X, 0, -m_vPlotArea.Z) * (0.5f + m_bbAreaScale - 1.0f);
                markerPos.Z -= pos * m_vPlotArea.Z;
                m_MZMarkerPositions.Add(markerPos); //save marker position
                Vector3 secondPoint = markerPos + new Vector3(-4, -1.5f, 0);
                vertices[0].Position = markerPos;
                vertices[0].Color = Color.Black.ToArgb();

                vertices[1].Position = secondPoint;
                vertices[1].Color = Color.Black.ToArgb();

                d3dDevice.DrawUserPrimitives(PrimitiveType.LineList, 1, vertices);

                secondPoint.X -= 2;
                Vector2 vPos = WorldToScreen(secondPoint);
                m_axisFont.DrawText(null, mz.ToString("F01"), new Point((int)(vPos.X + mzXOffset), (int)(vPos.Y + mzYOffset)), m_ColorFontCurrent);

            }



        }




        /// <summary>
        /// Renders a semi-transparent box, encapsulating the plot
        /// </summary>
        void RenderBoundingBox()
        {

            d3dDevice.RenderState.Lighting = false;

            d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;



            //d3dDevice.RenderState.ZBufferWriteEnable = false;
            //d3dDevice.SetTextureStageState(0, TextureStageStates.ColorOperation, (int)TextureOperation.BlendDiffuseAlpha);
            //d3dDevice.SetTextureStageState(0, TextureStageStates.ColorArgument1, (int)TextureArgument.Diffuse);
            //d3dDevice.SetTextureStageState(0, TextureStageStates.ColorArgument2, (int)TextureArgument.Current);
            //d3dDevice.SetTextureStageState(0, TextureStageStates.AlphaOperation, (int)TextureOperation.SelectArg1);
            //d3dDevice.SetTextureStageState(0, TextureStageStates.AlphaArgument1, (int)TextureArgument.Diffuse);
            //d3dDevice.SetTextureStageState(0, TextureStageStates.AlphaArgument2, (int)TextureArgument.Current);

            //d3dDevice.SetTextureStageState(1, TextureStageStates.ColorOperation, (int)TextureOperation.Disable);
            //d3dDevice.SetTextureStageState(1, TextureStageStates.AlphaOperation, (int)TextureOperation.Disable);



            //pDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
            //pDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
            //pDev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
            //pDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);



            d3dDevice.Transform.World = Matrix.Scaling(m_vPlotArea * (0.5f + m_bbAreaScale - 1.0f)) *
                Matrix.Translation(m_vPlotPosition.X, m_vPlotPosition.Y + m_vPlotArea.Y / 2, m_vPlotPosition.Z);

            d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
            d3dDevice.SetStreamSource(0, m_vbPlotArea, 0);
            d3dDevice.Indices = m_ibPlotAreaBBLines;
            d3dDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, 8, 0, 12);

            if (!m_bRenderBoundingBox)
                return;


            d3dDevice.RenderState.AlphaBlendEnable = true;
            d3dDevice.RenderState.CullMode = Cull.Clockwise;
            d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
            d3dDevice.Indices = m_ibPlotAreaBBTriangles;
            d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);




            d3dDevice.RenderState.AlphaBlendEnable = false;
            //d3dDevice.RenderState.ZBufferWriteEnable = true;


        }

        /// <summary>
        /// Renders a semi-transparent box, encapsulating the plot
        /// </summary>
        void RenderSelection()
        {
            if (m_vSelectionStart == m_vSelectionEnd) return;

            d3dDevice.RenderState.Lighting = false;

            d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            d3dDevice.RenderState.ZBufferWriteEnable = false;
            d3dDevice.RenderState.ZBufferEnable = true;
            d3dDevice.RenderState.AlphaBlendEnable = true;
            d3dDevice.RenderState.CullMode = Cull.CounterClockwise;


            d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;

            Vector3 vCenter = (m_vSelectionStart + m_vSelectionEnd) * 0.5f;


            d3dDevice.Transform.World = Matrix.Scaling(Math.Abs(m_vSelectionStart.X - vCenter.X), m_vPlotArea.Y / 2, Math.Abs(m_vSelectionStart.Z - vCenter.Z)) *
                Matrix.Translation(vCenter.X, m_vPlotPosition.Y + m_vPlotArea.Y / 2, vCenter.Z);



            d3dDevice.SetStreamSource(0, m_vbSelection, 0);
            d3dDevice.Indices = m_ibPlotAreaBBTriangles;
            d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);

            d3dDevice.RenderState.ColorVertex = false;
            d3dDevice.RenderState.DiffuseMaterialSource = ColorSource.Material;
            Material mat = new Material();
            mat.Diffuse = Color.Black;
            mat.Ambient = Color.Black;
            d3dDevice.Material = mat;
            d3dDevice.RenderState.Lighting = true;
            d3dDevice.SetStreamSource(0, m_vbPlotArea, 0);
            d3dDevice.Indices = m_ibPlotAreaBBLines;
            d3dDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, 6, 0, 12);

            d3dDevice.RenderState.ColorVertex = true;
            d3dDevice.RenderState.Lighting = false;
            d3dDevice.RenderState.AlphaBlendEnable = false;
            d3dDevice.RenderState.ZBufferWriteEnable = true;
            d3dDevice.RenderState.ZBufferEnable = true;


        }



        void RenderSelectionBalls()
        {
            if (m_SelectedPoints.Count == 0 && !m_bVertexSelected && !m_bDoubleSelection)
            {
                //d3dDevice.Transform.World = Matrix.Translation(0, 30, 0);
                //m_mVertexSelectionMesh.DrawSubset(0);
            }
            else
            {
                d3dDevice.SetTexture(0, null);
                Material mat = new Material();
                mat.Diffuse = Color.Red;
                d3dDevice.Material = mat;
                d3dDevice.RenderState.Lighting = true;
                d3dDevice.RenderState.DiffuseMaterialSource = ColorSource.Material;
                for (int i = 0; i < m_SelectedPoints.Count; i++)
                {
                    Vector3 vTransformedPos = m_SelectedPoints[i].m_vPosition;
                    vTransformedPos.TransformCoordinate(m_PlotTransform);

                    if (vTransformedPos.X >= -m_vPlotArea.X / 2 && vTransformedPos.X <= m_vPlotArea.X / 2 &&
                        vTransformedPos.Z >= -m_vPlotArea.Z / 2 && vTransformedPos.Z <= m_vPlotArea.Z / 2)
                    {
                        d3dDevice.Transform.World = Matrix.Translation(vTransformedPos);

                        m_mVertexSelectionMesh.DrawSubset(0);

                        Vector2 screenPos = WorldToScreen(vTransformedPos);
                        if (m_bDoubleSelection && m_SelectedPoints[i].m_vPosition == m_vSelectedPosition)
                        {
                            m_axisFont.DrawText(null, "Intensity: " + m_SelectedPoints[i].m_DataPoint.Intensity.ToString("F01") + "\nRT: "
                                                        + m_SelectedPoints[i].m_fRetentionTime.ToString("F01") + "\nM/Z: "
                                                        + m_SelectedPoints[i].m_DataPoint.MZ.ToString("F01"), new Point((int)screenPos.X + 7, (int)screenPos.Y - 23), Color.Blue.ToArgb());
                        }
                        else
                        {
                            m_axisFont.DrawText(null, "Intensity: " + m_SelectedPoints[i].m_DataPoint.Intensity.ToString("F01") + "\nRT: "
                            + m_SelectedPoints[i].m_fRetentionTime.ToString("F01") + "\nM/Z: "
                            + m_SelectedPoints[i].m_DataPoint.MZ.ToString("F01"), new Point((int)screenPos.X + 7, (int)screenPos.Y - 23), m_ColorFontCurrent.ToArgb());
                        }
                    }
                }

                if (m_bDoubleSelection)
                {
                    Vector3 vTransformedPos = m_vSelectedPosition;
                    vTransformedPos.TransformCoordinate(m_PlotTransform);

                    mat.Diffuse = Color.Blue;
                    d3dDevice.Material = mat;
                    if (vTransformedPos.X >= -m_vPlotArea.X / 2 && vTransformedPos.X <= m_vPlotArea.X / 2 &&
                        vTransformedPos.Z >= -m_vPlotArea.Z / 2 && vTransformedPos.Z <= m_vPlotArea.Z / 2)
                    {
                        d3dDevice.Transform.World = Matrix.Translation(vTransformedPos);

                        m_mVertexSelectionMesh.DrawSubset(0);

                    }
                }
                else if (m_bVertexSelected)
                {
                    Vector3 vTransformedPos = m_vSelectedPosition;
                    vTransformedPos.TransformCoordinate(m_PlotTransform);

                    if (vTransformedPos.X >= -m_vPlotArea.X / 2 && vTransformedPos.X <= m_vPlotArea.X / 2 &&
                        vTransformedPos.Z >= -m_vPlotArea.Z / 2 && vTransformedPos.Z <= m_vPlotArea.Z / 2)
                    {
                        d3dDevice.Transform.World = Matrix.Translation(vTransformedPos);
                        //d3dDevice.RenderState.AlphaBlendEnable = true;
                        //d3dDevice.RenderState.SourceBlend = Blend.BlendFactor;
                        //d3dDevice.RenderState.DestinationBlend = Blend.InvBlendFactor;
                        //d3dDevice.RenderState.BlendFactor = Color.FromArgb(150, 255, 0, 0);
                        m_mVertexSelectionMesh.DrawSubset(0);
                        //d3dDevice.RenderState.AlphaBlendEnable = false;
                    }
                }
                d3dDevice.RenderState.Lighting = false;
            }


        }

        /// <summary>
        /// Renders grid on visible sides of the bounding box
        /// </summary>
        void RenderGrid()
        {
            if (m_PlotData == null) return;
            if (m_vbPlotVertices == null) return;
            if (!m_PlotData.PlotSettings.ViewSettings.ShowGrid) return;

            d3dDevice.RenderState.ZBufferEnable = false;
            d3dDevice.RenderState.AntiAliasedLineEnable = true;
            d3dDevice.Transform.World = Matrix.Identity;
            d3dDevice.RenderState.Lighting = false;
            d3dDevice.RenderState.AlphaBlendEnable = true;
            d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            d3dDevice.SamplerState[0].AddressU = TextureAddress.Wrap;
            Vector3 viewDirection = m_vLookAt - m_vCameraPos;
            Vector3 toCamera, normal;
            //left side
            if (m_PlotData.PlotSettings.ViewSettings.ProjectionType == ProjectionType.Perspective)
                toCamera = m_vCameraPos - (m_vPlotPosition + new Vector3(-m_vPlotArea.X / 2, 0, 0));
            else
            {
                toCamera = m_vCameraPos - m_vLookAt;
            }
            normal = new Vector3(1, 0, 0);
            if (Vector3.Dot(normal, toCamera) > 0)
            {
                //side visible, draw grid

                //intensity
                for (int i = 1; i < m_NumIntensityMarkers - 1; i++)
                {
                    RenderDashedLine(m_IntensityMarkerPoints[2 * i].Position,
                        m_IntensityMarkerPoints[2 * i].Position + new Vector3(0, 0, -m_vPlotArea.Z));
                }

                for (int i = 0; i < m_MZMarkerPositions.Count; i++)
                {
                    RenderDashedLine(m_MZMarkerPositions[i],
                        m_MZMarkerPositions[i] + new Vector3(0, m_vPlotArea.Y, 0));
                }

            }


            //back side

            if (m_PlotData.PlotSettings.ViewSettings.ProjectionType == ProjectionType.Perspective)
                toCamera = m_vCameraPos - (m_vPlotPosition + new Vector3(0, 0, -m_vPlotArea.Z / 2));
            else
            {
                toCamera = m_vCameraPos - m_vLookAt;
            }
            normal = new Vector3(0, 0, 1);
            if (Vector3.Dot(normal, toCamera) > 0)
            {
                //side visible, draw grid

                //intensity
                for (int i = 1; i < m_NumIntensityMarkers - 1; i++)
                {
                    RenderDashedLine(m_IntensityMarkerPoints[2 * i].Position + new Vector3(0, 0, -m_vPlotArea.Z - 2),
                        m_IntensityMarkerPoints[2 * i].Position + new Vector3(m_vPlotArea.X + 2, 0, -m_vPlotArea.Z - 2));
                }

                for (int i = 0; i < m_RTMarkerPositions.Count; i++)
                {
                    RenderDashedLine(m_RTMarkerPositions[i] + new Vector3(0, 0, -m_vPlotArea.Z - 2),
                        m_RTMarkerPositions[i] + new Vector3(0, m_vPlotArea.Y + 0.5f, -m_vPlotArea.Z - 2));
                }

            }

            //right side

            if (m_PlotData.PlotSettings.ViewSettings.ProjectionType == ProjectionType.Perspective)
                toCamera = m_vCameraPos - (m_vPlotPosition + new Vector3(m_vPlotArea.X / 2, 0, 0));
            else
            {
                toCamera = m_vCameraPos - m_vLookAt;
            }
            normal = new Vector3(-1, 0, 0);
            if (Vector3.Dot(normal, toCamera) > 0)
            {
                //side visible, draw grid

                //intensity
                for (int i = 1; i < m_NumIntensityMarkers - 1; i++)
                {
                    RenderDashedLine(m_IntensityMarkerPoints[2 * i].Position + new Vector3(m_vPlotArea.X + 2, 0, -m_vPlotArea.Z - 2),
                        m_IntensityMarkerPoints[2 * i].Position + new Vector3(m_vPlotArea.X + 2, 0, 0));
                }

                for (int i = 0; i < m_MZMarkerPositions.Count; i++)
                {
                    RenderDashedLine(m_MZMarkerPositions[i] + new Vector3(m_vPlotArea.X + 2, 0, 0),
                        m_MZMarkerPositions[i] + new Vector3(m_vPlotArea.X + 2, m_vPlotArea.Y + 0.5f, 0));
                }

            }

            //front side

            if (m_PlotData.PlotSettings.ViewSettings.ProjectionType == ProjectionType.Perspective)
                toCamera = m_vCameraPos - (m_vPlotPosition + new Vector3(0, 0, m_vPlotArea.Z / 2));
            else
            {
                toCamera = m_vCameraPos - m_vLookAt;
            }
            normal = new Vector3(0, 0, -1);
            if (Vector3.Dot(normal, toCamera) > 0)
            {
                //side visible, draw grid

                //intensity
                for (int i = 1; i < m_NumIntensityMarkers - 1; i++)
                {
                    RenderDashedLine(m_IntensityMarkerPoints[2 * i].Position,
                        m_IntensityMarkerPoints[2 * i].Position + new Vector3(m_vPlotArea.X, 0, 0));
                }

                for (int i = 0; i < m_RTMarkerPositions.Count; i++)
                {
                    RenderDashedLine(m_RTMarkerPositions[i],
                        m_RTMarkerPositions[i] + new Vector3(0, m_vPlotArea.Y, 0));
                }

            }


            //grid on the ground
            //intensity
            for (int i = 0; i < m_RTMarkerPositions.Count; i++)
            {
                RenderDashedLine(m_RTMarkerPositions[i],
                    m_RTMarkerPositions[i] + new Vector3(0, 0, -m_vPlotArea.Z));
            }

            for (int i = 0; i < m_MZMarkerPositions.Count; i++)
            {
                RenderDashedLine(m_MZMarkerPositions[i],
                    m_MZMarkerPositions[i] + new Vector3(m_vPlotArea.X, 0, 0));
            }

            d3dDevice.RenderState.ZBufferEnable = true;
            d3dDevice.RenderState.AlphaBlendEnable = false;
            d3dDevice.RenderState.AntiAliasedLineEnable = false;
        }


        /// <summary>
        /// Renders text with debugging info
        /// </summary>
        void RenderDebugText()
        {
            //return;
            m_debugFont.DrawText(null, "BackBufferSize:" + currentPresentParameters.BackBufferWidth +
                " x " + currentPresentParameters.BackBufferHeight + " Frame#:" + m_iFrameNumber +
            "RX:" + m_fCameraRotationX.ToString() + " RY:" + m_fCameraRotationY.ToString(), new Point(0, 0), Color.Red);

        }

        void RenderLegend()
        {

            const int xStart = 10;
            const int yStart = 166;
            if (m_GradientTexture == null) return;
            d3dDevice.RenderState.ZBufferEnable = false;
            m_spriteObject.Begin(SpriteFlags.None);

            m_spriteObject.Transform = Matrix.Transformation2D(new Vector2(), 0.0f,
                                        new Vector2(0.3f, 20.0f), new Vector2(),
                                        -(float)Math.PI / 2, new Vector2(xStart, yStart));
            m_spriteObject.Draw(m_GradientTexture, System.Drawing.Rectangle.Empty, new Vector3(0, 0, 0), new Vector3(0, 0, 1), Color.White.ToArgb());
            m_spriteObject.End();
            d3dDevice.RenderState.ZBufferEnable = true;
            m_axisFont.DrawText(null, m_PlotData.MinIntensity.ToString("F01"),
                    new Point(xStart + 24, yStart - 6), m_ColorFontCurrent);

            m_axisFont.DrawText(null, (m_PlotData.MaxIntensity / 2 - m_PlotData.MinIntensity / 2).ToString("F01"),
                new Point(xStart + 24, 83), m_ColorFontCurrent);

            m_axisFont.DrawText(null, m_PlotData.MaxIntensity.ToString("F01"),
                new Point(xStart + 24, 6), m_ColorFontCurrent);

        }

        void RenderLoadingScreen()
        {
            if (m_WhiteTexture == null) return;

            d3dDevice.RenderState.ZBufferEnable = false;
            d3dDevice.RenderState.AlphaBlendEnable = true;
            d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            m_spriteObject.Begin(SpriteFlags.None);

            m_spriteObject.Draw2D(m_WhiteTexture, System.Drawing.Rectangle.Empty,
                new Rectangle(0, 0, currentPresentParameters.BackBufferWidth, currentPresentParameters.BackBufferHeight), new Point(0, 0), 0, new Point(0, 0), Color.FromArgb(150, 0, 0, 0));

            m_spriteObject.End();

            d3dDevice.RenderState.ZBufferEnable = true;
            d3dDevice.RenderState.AlphaBlendEnable = false;
            m_LoadingFont.DrawText(null, "Loading...", new Point(currentPresentParameters.BackBufferWidth / 2 - 50, currentPresentParameters.BackBufferHeight / 2 - 15),
                Color.White);


        }

        void RenderErrorText()
        {
            if (m_WhiteTexture == null) return;

            d3dDevice.RenderState.ZBufferEnable = false;
            d3dDevice.RenderState.AlphaBlendEnable = true;
            d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            m_spriteObject.Begin(SpriteFlags.None);

            m_spriteObject.Draw2D(m_WhiteTexture, System.Drawing.Rectangle.Empty,
                new Rectangle(0, 0, currentPresentParameters.BackBufferWidth, currentPresentParameters.BackBufferHeight), new Point(0, 0), 0, new Point(0, 0), Color.FromArgb(150, 0, 0, 0));

            m_spriteObject.End();

            d3dDevice.RenderState.ZBufferEnable = true;
            d3dDevice.RenderState.AlphaBlendEnable = false;
            m_LoadingFont.DrawText(null, m_ErrorText, new Rectangle(0, 0, currentPresentParameters.BackBufferWidth, currentPresentParameters.BackBufferHeight), DrawTextFormat.Center,
                Color.White);


        }

        ///// <summary>
        ///// Render Plot from current time
        ///// </summary>
        //void RenderPlot()
        //{
        //    if (m_PlotData == null) return;
        //    d3dDevice.Transform.World = Matrix.Identity;

        //    //////////////////////////////////////////////////////////////////////////
        //    //first, render bb to set stencil mask to bb area
        //    d3dDevice.RenderState.StencilEnable = true;
        //    d3dDevice.RenderState.StencilFunction = Compare.Always;
        //    d3dDevice.RenderState.StencilPass = StencilOperation.Increment;

        //    d3dDevice.RenderState.CullMode = Cull.CounterClockwise;
        //    d3dDevice.RenderState.ZBufferEnable = true;
        //    d3dDevice.RenderState.ZBufferWriteEnable = true;
        //    d3dDevice.RenderState.ColorWriteEnable = 0;

        //    d3dDevice.Transform.World = Matrix.Scaling(m_vPlotArea * (0.5f)) *
        //       Matrix.Translation(m_vPlotPosition.X, m_vPlotPosition.Y + m_vPlotArea.Y / 2, m_vPlotPosition.Z);
        //    d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
        //    d3dDevice.Indices = m_ibPlotAreaBBTriangles;
        //    d3dDevice.SetStreamSource(0, m_vbPlotArea, 0);
        //    d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);



        //    //////////////////////////////////////////////////////////////////////////
        //    d3dDevice.RenderState.ZBufferEnable = true;
        //    d3dDevice.RenderState.ZBufferWriteEnable = false;
        //    //d3dDevice.RenderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha;
        //    //ok, render plot data with reversed zbuffer and stencil mask
        //    d3dDevice.RenderState.ReferenceStencil = 0;
        //    d3dDevice.RenderState.StencilFunction = Compare.Less;



        //    float fScale = 1.0f / m_PlotZoom.m_fMZSubset;
        //    d3dDevice.Transform.World = Matrix.Translation((-m_PlotZoom.m_fRTOffset -m_PlotZoom.m_fRTSubset/2 + 0.5f) *
        //        m_vPlotArea.X, 0, (m_PlotZoom.m_fMZOffset + m_PlotZoom.m_fMZSubset / 2 - 0.5f) * m_vPlotArea.Z) * Matrix.Scaling(fScale, 1, fScale);


        //    //render plot, but with reversed zbuffer and colorwrite off
        //    d3dDevice.RenderState.ZBufferFunction = Compare.Greater;

        //    switch (m_eCurrentPlotType)
        //    {
        //        case PlotTypes.Points:
        //            RenderPointsPlot();
        //            break;
        //        case PlotTypes.Lines:
        //            RenderLinesPlot();
        //            break;
        //        case PlotTypes.Surface:
        //            RenderSurfacePlot();
        //            break;
        //    }

        //    //render back bb
        //    d3dDevice.RenderState.StencilEnable = false;

        //    d3dDevice.RenderState.CullMode = Cull.Clockwise;
        //    d3dDevice.RenderState.ZBufferEnable = true;
        //    d3dDevice.RenderState.ZBufferWriteEnable = true;
        //    d3dDevice.RenderState.ColorWriteEnable = 0;

        //    d3dDevice.Transform.World = Matrix.Scaling(m_vPlotArea * (0.5f)) *
        //       Matrix.Translation(m_vPlotPosition.X, m_vPlotPosition.Y + m_vPlotArea.Y / 2, m_vPlotPosition.Z);
        //    d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
        //    d3dDevice.Indices = m_ibPlotAreaBBTriangles;
        //    d3dDevice.SetStreamSource(0, m_vbPlotArea, 0);
        //    d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);


        //    d3dDevice.RenderState.ZBufferEnable = true;
        //    d3dDevice.RenderState.ZBufferWriteEnable = true;

        //    //render plot, normally this time
        //    fScale = 1.0f / m_PlotZoom.m_fMZSubset;
        //    d3dDevice.Transform.World = Matrix.Translation((-m_PlotZoom.m_fRTOffset - m_PlotZoom.m_fRTSubset / 2 + 0.5f) *
        //        m_vPlotArea.X, 0, (m_PlotZoom.m_fMZOffset + m_PlotZoom.m_fMZSubset / 2 - 0.5f) * m_vPlotArea.Z) * Matrix.Scaling(fScale, 1, fScale);


        //    //render plot, but with reversed zbuffer and colorwrite off
        //    d3dDevice.RenderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha;
        //    d3dDevice.RenderState.ZBufferFunction = Compare.LessEqual;


        //    d3dDevice.RenderState.StencilEnable = true;
        //    d3dDevice.RenderState.StencilFunction = Compare.Less;
        //    d3dDevice.RenderState.StencilPass = StencilOperation.Keep;
        //    d3dDevice.RenderState.ReferenceStencil = 1;

        //    d3dDevice.RenderState.CullMode = Cull.Clockwise;

        //    switch (m_eCurrentPlotType)
        //    {
        //        case PlotTypes.Points:
        //            RenderPointsPlot();
        //            break;
        //        case PlotTypes.Lines:
        //            RenderLinesPlot();
        //            break;
        //        case PlotTypes.Surface:
        //            RenderSurfacePlot();
        //            break;
        //    }
        //    d3dDevice.RenderState.StencilEnable = false;
        //}

        /// <summary>
        /// Renders the Plot
        /// </summary>
        void RenderPlot()
        {
            if (m_PlotData == null) return;
            d3dDevice.Transform.World = Matrix.Identity;



            Plane p0 = Plane.FromPointNormal(m_vPlotPosition + new Vector3(-m_vPlotArea.X / 2, 0, 0), new Vector3(1, 0, 0));
            Plane p1 = Plane.FromPointNormal(m_vPlotPosition + new Vector3(m_vPlotArea.X / 2, 0, 0), new Vector3(-1, 0, 0));
            //Plane p2 = Plane.FromPointNormal(m_vPlotPosition + new Vector3(0, 0, 0), new Vector3(0, 1, 0));
            //Plane p3 = Plane.FromPointNormal(m_vPlotPosition + new Vector3(0, m_vPlotArea.Y, 0), new Vector3(0, -1, 0));
            Plane p4 = Plane.FromPointNormal(m_vPlotPosition + new Vector3(0, 0, m_vPlotArea.Z / 2), new Vector3(0, 0, -1));
            Plane p5 = Plane.FromPointNormal(m_vPlotPosition + new Vector3(0, 0, -m_vPlotArea.Z / 2), new Vector3(0, 0, 1));

            d3dDevice.ClipPlanes[0].Plane = p0;
            d3dDevice.ClipPlanes[0].Enabled = true;
            d3dDevice.ClipPlanes[1].Plane = p1;
            d3dDevice.ClipPlanes[1].Enabled = true;
            //d3dDevice.ClipPlanes[2].Plane = p2;
            //d3dDevice.ClipPlanes[2].Enabled = true;
            //d3dDevice.ClipPlanes[3].Plane = p3;
            //d3dDevice.ClipPlanes[3].Enabled = true;
            d3dDevice.ClipPlanes[2].Plane = p4;
            d3dDevice.ClipPlanes[2].Enabled = true;
            d3dDevice.ClipPlanes[3].Plane = p5;
            d3dDevice.ClipPlanes[3].Enabled = true;


            d3dDevice.SamplerState[0].AddressU = TextureAddress.Clamp;

            //float fScale = 1.0f / m_PlotZoom.m_fMZSubset;


            m_PlotTransform = Matrix.Translation((-m_PlotZoom.m_fRTOffset - m_PlotZoom.m_fRTSubset / 2 + 0.5f) *
                m_vPlotArea.X, 0, (m_PlotZoom.m_fMZOffset + m_PlotZoom.m_fMZSubset / 2 - 0.5f) * m_vPlotArea.Z) *
                Matrix.Scaling(1.0f / m_PlotZoom.m_fRTSubset, 1, 1.0f / m_PlotZoom.m_fMZSubset);

            d3dDevice.Transform.World = m_PlotTransform;
            switch (m_eCurrentPlotType)
            {
                case PlotType.Points:
                    RenderPointsPlot();
                    break;
                case PlotType.Lines:
                    RenderLinesPlot();
                    break;
                case PlotType.Surface:
                    if (m_bUseShaders)
                    {
                        RenderSurfacePlotShader();
                    }
                    else
                    {
                        RenderSurfacePlot();
                    }

                    break;
            }


            d3dDevice.ClipPlanes[0].Enabled = false;
            d3dDevice.ClipPlanes[1].Enabled = false;
            d3dDevice.ClipPlanes[2].Enabled = false;
            d3dDevice.ClipPlanes[3].Enabled = false;
            //d3dDevice.ClipPlanes[4].Enabled = false;
            //d3dDevice.ClipPlanes[5].Enabled = false;


        }

        /// <summary>
        /// Renders the Surface Plot
        /// </summary>
        private void RenderSurfacePlot()
        {
            //d3dDevice.RenderState.Lighting = false;
            if (m_vbPlotVertices == null || m_ibPlotIndices == null)
                return;
            d3dDevice.RenderState.Lighting = true;

            d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            d3dDevice.RenderState.AlphaBlendEnable = true;

            d3dDevice.Lights[0].Type = LightType.Directional;
            d3dDevice.Lights[0].Diffuse = Color.White;
            d3dDevice.Lights[0].Ambient = Color.DarkGray;
            d3dDevice.Lights[0].Direction = new Vector3(0f, -0.3f, -1f);
            d3dDevice.Lights[0].Enabled = true;

            //d3dDevice.RenderState.Ambient = Color.LightGray;
            d3dDevice.RenderState.NormalizeNormals = true;
            d3dDevice.RenderState.CullMode = Cull.None;
            d3dDevice.SetStreamSource(0, m_vbPlotVertices, 0);
            d3dDevice.Indices = m_ibPlotIndices;
            d3dDevice.SetTexture(0, m_GradientTexture);

            Material mat = new Material();
            mat.Diffuse = Color.White;
            mat.Ambient = Color.DarkGray;
            d3dDevice.Material = mat;


            d3dDevice.VertexFormat = CustomVertex.PositionNormalTextured.Format;
            d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                m_iMzGrid * m_PlotData.RetentionScansCount, 0, 2 * (m_iMzGrid - 1) * (m_PlotData.RetentionScansCount - 1));

            d3dDevice.SetTexture(0, null);
            d3dDevice.RenderState.AlphaBlendEnable = false;

        }

        /// <summary>
        /// Renders the Surface Plot
        /// </summary>
        private void RenderSurfacePlotShader()
        {
            //d3dDevice.RenderState.Lighting = false;
            if (m_vbPlotVertices == null || m_ibPlotIndices == null || m_effectSurface == null)
                return;



            // set states
            d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            d3dDevice.RenderState.AlphaBlendEnable = true;
            d3dDevice.RenderState.AlphaTestEnable = true;
            d3dDevice.RenderState.AlphaFunction = Compare.Greater;
            d3dDevice.RenderState.ReferenceAlpha = 0;
            d3dDevice.RenderState.CullMode = Cull.Clockwise;
            d3dDevice.SetStreamSource(0, m_vbPlotVertices, 0);
            d3dDevice.Indices = m_ibPlotIndices;
            d3dDevice.VertexFormat = CustomVertex.PositionNormalTextured.Format;

            // set parameters
            m_effectSurface.SetValue("ViewProj", m_ViewMatrix * m_ProjectionMatrix);
            Matrix world = d3dDevice.Transform.World;

            m_effectSurface.SetValue("World", world);
            m_effectSurface.SetValue("WorldIT", Matrix.TransposeMatrix(Matrix.Invert(world)));
            m_effectSurface.SetValue("UseSpecular", m_PlotData.PlotSettings.ViewSettings.UseSpecular);
            m_effectSurface.SetValue("LightDirection", new Vector4(0f, -0.3f, 1f, 0.0f));
            m_effectSurface.SetValue("CameraPosition", new Vector4(m_vCameraPos.X, m_vCameraPos.Y, m_vCameraPos.Z, 1.0f));
            m_effectSurface.SetValue("xTexture", m_GradientTexture);
            m_effectSurface.SetValue("xAmbient", 0.5f);
            m_effectSurface.Technique = "simple";
            m_effectSurface.SetValue("DrawWhat", true);

            // draw

            m_effectSurface.Begin(0);
            m_effectSurface.BeginPass(0);
            try
            {
                d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                m_iMzGrid * m_PlotData.RetentionScansCount, 0, 2 * (m_iMzGrid - 1) * (m_PlotData.RetentionScansCount - 1));

            }
            catch (InvalidCallException)
            {
                debugLog.Write("DrawIndexedPrimitives failed when drawing main surface plot");

            }
            m_effectSurface.EndPass();
            m_effectSurface.End();

            // zpass
            m_effectSurface.SetValue("DrawWhat", false);
            d3dDevice.RenderState.ColorWriteEnable = 0;
            m_effectSurface.Begin(0);
            m_effectSurface.BeginPass(0);
            try
            {
                d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                m_iMzGrid * m_PlotData.RetentionScansCount, 0, 2 * (m_iMzGrid - 1) * (m_PlotData.RetentionScansCount - 1));

            }
            catch (InvalidCallException)
            {
                debugLog.Write("DrawIndexedPrimitives failed when drawing surface plot z pass");
            }
            m_effectSurface.EndPass();
            m_effectSurface.End();

            d3dDevice.RenderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha;
            m_effectSurface.Begin(0);
            m_effectSurface.BeginPass(0);
            try
            {
                d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                m_iMzGrid * m_PlotData.RetentionScansCount, 0, 2 * (m_iMzGrid - 1) * (m_PlotData.RetentionScansCount - 1));

            }
            catch (InvalidCallException)
            {
                debugLog.Write("DrawIndexedPrimitives failed when drawing gray surface plot");
            }
            m_effectSurface.EndPass();
            m_effectSurface.End();

            // reset states
            d3dDevice.SetTexture(0, null);
            d3dDevice.RenderState.CullMode = Cull.Clockwise;
            d3dDevice.RenderState.AlphaBlendEnable = false;
            d3dDevice.RenderState.AlphaTestEnable = false;
            d3dDevice.RenderState.AlphaBlendEnable = false;

        }
        /// <summary>
        /// Renders the Lines Plot
        /// </summary>
        private void RenderLinesPlot()
        {
            d3dDevice.RenderState.Lighting = false;
            if (m_vbPlotVertices == null)
                return;
            int iIndexNewLine = 0;
            d3dDevice.SetStreamSource(0, m_vbPlotVertices, 0);
            for (int i = 0; i < m_LinePlotData.Count; i++)
            {
                //if (m_LinePlotData[i] <= 1) continue;
                d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
                try
                {
                    d3dDevice.DrawPrimitives(PrimitiveType.LineStrip, iIndexNewLine, m_LinePlotData[i] + 1);
                }
                catch (InvalidCallException)
                {
                    debugLog.Write("DrawPrimitives failed while drawing lines in Lines plot");
                }

                iIndexNewLine += m_LinePlotData[i] + 2;
            }
            if (m_PlotData.PlotSettings.OverlapLines)
            {
                if (m_ibPlotIndices == null)
                    return;
                d3dDevice.RenderState.Lighting = true;
                d3dDevice.RenderState.Ambient = System.Drawing.Color.FromArgb(255, 255, 255, 255);
                d3dDevice.RenderState.SourceBlend = Blend.SourceAlpha;
                d3dDevice.RenderState.DestinationBlend = Blend.InvSourceAlpha;
                d3dDevice.RenderState.AlphaBlendEnable = true;
                d3dDevice.RenderState.CullMode = Cull.None;

                d3dDevice.RenderState.DiffuseMaterialSource = ColorSource.Material;
                //d3dDevice.RenderState.ColorVertex = false;
                Material mat = new Material();
                mat.Diffuse = Color.FromArgb(50, Color.DarkGray);
                mat.Ambient = Color.FromArgb(50, Color.DarkGray);
                //mat.Specular = Color.FromArgb(50, Color.DarkGray);
                //mat.Emissive = Color.FromArgb(50, Color.DarkGray);
                d3dDevice.Material = mat;
                d3dDevice.Indices = m_ibPlotIndices;
                //int[] array =(int[]) m_ibPlotIndices.Lock(0, typeof(int), LockFlags.None,m_ibPlotIndices.SizeInBytes/ sizeof(int)) ;
                //array[0]= array[0];
                //m_ibPlotIndices.Unlock();
                d3dDevice.VertexFormat = CustomVertex.PositionColored.Format;
                int newLineIdx = 0;

                int TotalNumber = 0;
                for (int i = 0; i < m_LinePlotData.Count; i++)
                {
                    TotalNumber += m_LinePlotData[i] + 2;
                }

                if (m_fCameraRotationY > Math.PI)
                {
                    for (int lineToDraw = m_LinePlotData.Count - 1; lineToDraw >= 0; lineToDraw--)
                    {
                        newLineIdx = 0;
                        for (int i = 0; i < m_LinePlotData.Count; i++)
                        {
                            if (i == lineToDraw)
                            {
                                try
                                {
                                    d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, TotalNumber * 2, newLineIdx, (m_LinePlotData[i] + 2) * 2 - 2);
                                }
                                catch (InvalidCallException)
                                {
                                    debugLog.Write("DrawIndexedPrimitives failed while drawing overlaps in Lines plot");
                                }
                            }


                            newLineIdx += (m_LinePlotData[i] + 2) * 2;
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < m_LinePlotData.Count; i++)
                    {
                        try
                        {
                            d3dDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, TotalNumber * 2, newLineIdx, (m_LinePlotData[i] + 2) * 2 - 2);
                        }
                        catch (InvalidCallException)
                        {
                            debugLog.Write("DrawIndexedPrimitives failed while drawing overlaps in Lines plot");
                        }

                        newLineIdx += (m_LinePlotData[i] + 2) * 2;
                    }
                }

                d3dDevice.RenderState.Lighting = false;
                d3dDevice.RenderState.CullMode = Cull.Clockwise;
                d3dDevice.RenderState.AlphaBlendEnable = false;

            }
        }

        /// <summary>
        /// Renders the Points Plot
        /// </summary>
        private void RenderPointsPlot()
        {
            d3dDevice.RenderState.Lighting = false;
            //nothing to draw
            if (m_vbPlotVertices == null)
                return;

            d3dDevice.VertexFormat = CustomVertex.PositionTextured.Format;

            d3dDevice.SetStreamSource(0, m_vbPlotVertices, 0);
            d3dDevice.SetTexture(0, m_GradientTexture);
            d3dDevice.Indices = m_ibPlotIndices;
            int nVertices = m_vbPlotVertices.SizeInBytes / (CustomVertex.PositionTextured.StrideSize);

            try
            {
                d3dDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, nVertices, 0, nVertices / 2);
            }
            catch (InvalidCallException)
            {
                debugLog.Write("DrawIndexedPrimitives failed when drawing Points plot");
            }


            d3dDevice.SetTexture(0, null);
        }

        #endregion



        internal void Plot3DControl_MouseWheel(object sender, MouseEventArgs e)
        {
            if (d3dDevice == null) return;

            float fZ = e.Delta / 2400.0f;
            m_PlotZoom.m_fMZSubset -= fZ;
            m_PlotZoom.m_fRTSubset -= fZ;

            const float fMinSubset = 0.05f;



            if (m_PlotZoom.m_fMZSubset > 1)
            {
                m_PlotZoom.m_fMZSubset = 1;
            }
            else if (m_PlotZoom.m_fMZSubset < fMinSubset)
            {
                m_PlotZoom.m_fMZSubset = fMinSubset;
            }
            else
                m_PlotZoom.m_fMZOffset += fZ / 2;


            if (m_PlotZoom.m_fRTSubset > 1)
                m_PlotZoom.m_fRTSubset = 1;
            else if (m_PlotZoom.m_fRTSubset < fMinSubset)
                m_PlotZoom.m_fRTSubset = fMinSubset;
            else
                m_PlotZoom.m_fRTOffset += fZ / 2;

            if (m_PlotZoom.m_fMZOffset < 0)
                m_PlotZoom.m_fMZOffset = 0;

            if (m_PlotZoom.m_fRTOffset < 0)
                m_PlotZoom.m_fRTOffset = 0;


            if (m_PlotZoom.m_fMZSubset + m_PlotZoom.m_fMZOffset > 1)
                m_PlotZoom.m_fMZOffset = 1 - m_PlotZoom.m_fMZSubset;


            if (m_PlotZoom.m_fRTSubset + m_PlotZoom.m_fRTOffset > 1)
                m_PlotZoom.m_fRTOffset = 1 - m_PlotZoom.m_fRTSubset;


            RenderD3D();



        }

        int m_iCurrentDataZoom = 0;

        public void SetDataZoom(int zoomFactor)
        {
            if (d3dDevice == null) return;

            //int zoomDelta = iZoom - m_iCurrentZoom;
            //m_iCurrentZoom = iZoom;
            if (zoomFactor < 0) zoomFactor = 0;
            m_iCurrentDataZoom = zoomFactor;


            Point mousePos = this.PointToClient(Cursor.Position);
            
            Vector3 groundPos = GetGroundLevelPosition(mousePos.X, mousePos.Y);

            float fCurrentMZ = m_PlotZoom.m_fMZSubset;
            float fCurrentRT = m_PlotZoom.m_fRTSubset;



            m_PlotZoom.m_fMZSubset = (float)Math.Pow(0.95f, zoomFactor);
            m_PlotZoom.m_fRTSubset = (float)Math.Pow(0.95f, zoomFactor);



            if (m_PlotZoom.m_fMZSubset > 1)
            {
                m_PlotZoom.m_fMZSubset = 1;
            }
            else if (m_PlotZoom.m_fMZSubset < m_fMinSubset)
            {
                m_PlotZoom.m_fMZSubset = m_fMinSubset;
            }
            else
            {
                if (groundPos.X >= m_vPlotPosition.X - m_vPlotArea.X / 2 &&
                    groundPos.X <= m_vPlotPosition.X + m_vPlotArea.X / 2 &&
                    groundPos.Z >= m_vPlotPosition.Z - m_vPlotArea.Z / 2 &&
                    groundPos.Z <= m_vPlotPosition.Z + m_vPlotArea.Z / 2)
                {
                    m_PlotZoom.m_fMZOffset += (fCurrentMZ - m_PlotZoom.m_fMZSubset) * (-groundPos.Z + m_vPlotArea.Z / 2) / m_vPlotArea.Z;
                }
                else
                    m_PlotZoom.m_fMZOffset += (fCurrentMZ - m_PlotZoom.m_fMZSubset) / 2;
            }


            if (m_PlotZoom.m_fRTSubset > 1)
            {
                m_PlotZoom.m_fRTSubset = 1;
            }
            else if (m_PlotZoom.m_fRTSubset < m_fMinSubset)
            {
                m_PlotZoom.m_fRTSubset = m_fMinSubset;
            }
            else
            {
                if (groundPos.X >= m_vPlotPosition.X - m_vPlotArea.X / 2 &&
                    groundPos.X <= m_vPlotPosition.X + m_vPlotArea.X / 2 &&
                    groundPos.Z >= m_vPlotPosition.Z - m_vPlotArea.Z / 2 &&
                    groundPos.Z <= m_vPlotPosition.Z + m_vPlotArea.Z / 2)
                {
                    m_PlotZoom.m_fRTOffset += (fCurrentRT - m_PlotZoom.m_fRTSubset)*(groundPos.X + m_vPlotArea.X / 2) / m_vPlotArea.X;
                }
                else
                    m_PlotZoom.m_fRTOffset += (fCurrentRT - m_PlotZoom.m_fRTSubset) / 2;
            }

            if (m_PlotZoom.m_fMZOffset < 0)
                m_PlotZoom.m_fMZOffset = 0;

            if (m_PlotZoom.m_fRTOffset < 0)
                m_PlotZoom.m_fRTOffset = 0;


            if (m_PlotZoom.m_fMZSubset + m_PlotZoom.m_fMZOffset > 1)
                m_PlotZoom.m_fMZOffset = 1 - m_PlotZoom.m_fMZSubset;


            if (m_PlotZoom.m_fRTSubset + m_PlotZoom.m_fRTOffset > 1)
                m_PlotZoom.m_fRTOffset = 1 - m_PlotZoom.m_fRTSubset;


            RenderD3D();
        }

        private void Plot3DControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (d3dDevice == null) return;
            if (m_PlotData == null) return;
            //left mouse button rotates the plot
            if (SelectionMode)
            {
                if (e.Button == MouseButtons.Left)
                {
                    m_vSelectionEnd = GetGroundLevelPosition(e.X, e.Y);
                    if (m_vSelectionEnd.X < m_vPlotPosition.X - m_vPlotArea.X / 2)
                        m_vSelectionEnd.X = m_vPlotPosition.X - m_vPlotArea.X / 2;
                    if (m_vSelectionEnd.X > m_vPlotPosition.X + m_vPlotArea.X / 2)
                        m_vSelectionEnd.X = m_vPlotPosition.X + m_vPlotArea.X / 2;
                    if (m_vSelectionEnd.Z < m_vPlotPosition.Z - m_vPlotArea.Z / 2)
                        m_vSelectionEnd.Z = m_vPlotPosition.Z - m_vPlotArea.Z / 2;
                    if (m_vSelectionEnd.Z > m_vPlotPosition.Z + m_vPlotArea.Z / 2)
                        m_vSelectionEnd.Z = m_vPlotPosition.Z + m_vPlotArea.Z / 2;

                    Refresh();
                }
                else if (e.Button == MouseButtons.Right)
                {

                    float fDeltaX = e.X - m_previousMousePosition.X;
                    float fDeltaY = e.Y - m_previousMousePosition.Y;
                    m_previousMousePosition = e.Location;

                    fDeltaX /= m_fMouseMoveSensitivity;
                    fDeltaY /= m_fMouseMoveSensitivity;

                    Vector3 vDelta = new Vector3(fDeltaX, 0, fDeltaY);
                    vDelta.TransformNormal(Matrix.RotationY(m_fCameraRotationY) * Matrix.RotationX(m_fCameraRotationX));

                    m_PlotZoom.m_fRTOffset -= vDelta.X * m_PlotZoom.m_fRTSubset;
                    if (m_PlotZoom.m_fRTOffset < 0)
                        m_PlotZoom.m_fRTOffset = 0;
                    else if (m_PlotZoom.m_fRTOffset + m_PlotZoom.m_fRTSubset > 1)
                        m_PlotZoom.m_fRTOffset = 1 - m_PlotZoom.m_fRTSubset;

                    m_PlotZoom.m_fMZOffset += vDelta.Z * m_PlotZoom.m_fMZSubset;
                    if (m_PlotZoom.m_fMZOffset < 0)
                        m_PlotZoom.m_fMZOffset = 0;
                    else if (m_PlotZoom.m_fMZOffset + m_PlotZoom.m_fMZSubset > 1)
                        m_PlotZoom.m_fMZOffset = 1 - m_PlotZoom.m_fMZSubset;
                    RenderD3D();
                }

            }
            else
            {
                if (e.Button == MouseButtons.Left)
                {

                    //camera rotation
                    m_fCameraRotationY += (-e.X + m_previousMousePosition.X) / m_fMouseRotateSensitivity;
                    m_fCameraRotationX += (-e.Y + m_previousMousePosition.Y) / m_fMouseRotateSensitivity;

                    if (m_PlotData != null && m_PlotData.PlotSettings.ViewSettings.ProjectionType == ProjectionType.Orthographic)
                    {
                        if (m_fCameraRotationX > 0.06f)
                            m_fCameraRotationX = 0.06f;
                    }
                    else
                    {
                        if (m_fCameraRotationX > 0.13f)
                            m_fCameraRotationX = 0.13f;
                    }

                    if (m_fCameraRotationX < -Math.PI / 2 + 0.1f)
                        m_fCameraRotationX = -(float)Math.PI / 2 + 0.1f;

                    if (m_fCameraRotationY > 2 * Math.PI)
                        m_fCameraRotationY -= 2 * (float)Math.PI;
                    if (m_fCameraRotationY < 0)
                        m_fCameraRotationY += 2 * (float)Math.PI;
                    m_previousMousePosition = e.Location;





                    SetCamera();
                    RenderD3D();
                }
                else if (e.Button == MouseButtons.Right)
                {

                    float fDeltaX = e.X - m_previousMousePosition.X;
                    float fDeltaY = e.Y - m_previousMousePosition.Y;
                    m_previousMousePosition = e.Location;

                    fDeltaX /= m_fMouseMoveSensitivity;
                    fDeltaY /= m_fMouseMoveSensitivity;

                    Vector3 vDelta = new Vector3(fDeltaX, 0, fDeltaY);
                    vDelta.TransformNormal(Matrix.RotationY(m_fCameraRotationY) * Matrix.RotationX(m_fCameraRotationX));

                    m_PlotZoom.m_fRTOffset -= vDelta.X * m_PlotZoom.m_fRTSubset;
                    if (m_PlotZoom.m_fRTOffset < 0)
                        m_PlotZoom.m_fRTOffset = 0;
                    else if (m_PlotZoom.m_fRTOffset + m_PlotZoom.m_fRTSubset > 1)
                        m_PlotZoom.m_fRTOffset = 1 - m_PlotZoom.m_fRTSubset;

                    m_PlotZoom.m_fMZOffset += vDelta.Z * m_PlotZoom.m_fMZSubset;
                    if (m_PlotZoom.m_fMZOffset < 0)
                        m_PlotZoom.m_fMZOffset = 0;
                    else if (m_PlotZoom.m_fMZOffset + m_PlotZoom.m_fMZSubset > 1)
                        m_PlotZoom.m_fMZOffset = 1 - m_PlotZoom.m_fMZSubset;
                    RenderD3D();
                }
                else if (e.Button == MouseButtons.None && m_bEnableVertexSelection)
                {

                    Vector3 ray0 = Vector3.Unproject(new Vector3(e.X, e.Y, 0), d3dDevice.Viewport, m_ProjectionMatrix, m_ViewMatrix, m_PlotTransform);
                    Vector3 ray1 = Vector3.Unproject(new Vector3(e.X, e.Y, 1), d3dDevice.Viewport, m_ProjectionMatrix, m_ViewMatrix, m_PlotTransform);

                    Vector3 normalizedDir = ray1 - ray0;
                    normalizedDir.Normalize();
                    bool bOldSelected = m_bVertexSelected;
                    bool bOldDoubleSelection = m_bDoubleSelection;
                    //check if already selected
                    Vector3 alreadySelected;
                    m_bDoubleSelection = CheckForDoubleSelection(e.X, e.Y, out alreadySelected);
                    if (m_bDoubleSelection)
                    {
                        m_bVertexSelected = false;
                        m_bDoubleSelection = true;
                        m_vSelectedPosition = alreadySelected;
                    }
                    else
                    {
                        //if not, then check if a new vertex is selected
                        float fMZRange = m_fSelectionSnapDistance * m_PlotZoom.m_fMZSubset * m_PlotData.MZRange;
                        float fRTRange = m_fSelectionSnapDistance * m_PlotZoom.m_fRTSubset * m_PlotData.RTRange;
                        float fIntRange = 2 * m_fSelectionSnapDistance * (m_PlotData.MaxIntensity - m_PlotData.MinIntensity);

                        m_bVertexSelected = SelectVertex(ray0, normalizedDir, fMZRange, fRTRange, fIntRange, out m_vSelectedPosition, out m_SelectedData, out m_fSelectedIntensity);
                        m_bDoubleSelection = false;
                    }





                    if (m_bVertexSelected || bOldSelected || m_bDoubleSelection || bOldDoubleSelection)
                        Refresh();

                }
            }

        }

        private Vector3 GetGroundLevelPosition(int mouseX, int mouseY)
        {
            Vector3 ray0 = Vector3.Unproject(new Vector3(mouseX, mouseY, 0), d3dDevice.Viewport, m_ProjectionMatrix, m_ViewMatrix, Matrix.Translation(m_vPlotPosition));
            Vector3 ray1 = Vector3.Unproject(new Vector3(mouseX, mouseY, 1), d3dDevice.Viewport, m_ProjectionMatrix, m_ViewMatrix, Matrix.Translation(m_vPlotPosition));

            Plane plane = Plane.FromPointNormal(m_vPlotPosition, new Vector3(0, 1, 0));

            Vector3 pos = Plane.IntersectLine(plane, ray1, ray0);
            return pos;

        }

        Point m_RMBDownPos;
        private void Plot3DControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (d3dDevice == null) return;


            //store rclick pos for context menu
            if (e.Button == MouseButtons.Right)
                m_RMBDownPos = e.Location;

            //store current mouse location
            m_previousMousePosition = e.Location;
                        

            if (m_bSelectionMode)
            {
                //data selection mode
                Vector3 clickPos = GetGroundLevelPosition(e.X, e.Y);
                if (clickPos.X >= m_vPlotPosition.X - m_vPlotArea.X / 2 &&
                    clickPos.X <= m_vPlotPosition.X + m_vPlotArea.X / 2 &&
                    clickPos.Z >= m_vPlotPosition.Z - m_vPlotArea.Z / 2 &&
                    clickPos.Z <= m_vPlotPosition.Z + m_vPlotArea.Z / 2)
                {
                    m_vSelectionStart = clickPos;
                    m_vSelectionEnd = clickPos;
                }
            }
            else
            {
                //add/remove selected vertex
                if (e.Button == MouseButtons.Left && m_bEnableVertexSelection)
                {
                    if (m_bVertexSelected)
                    {
                        //add vertex to list
                        //m_vSelectedPosition.TransformCoordinate(m_PlotTransform);
                        SelectedVertexData vData = new SelectedVertexData();
                        vData.m_DataPoint = m_SelectedData;
                        vData.m_fRetentionTime = m_fSelectedIntensity;
                        vData.m_vPosition = m_vSelectedPosition;
                        m_SelectedPoints.Add(vData);
                    }
                    else if (m_bDoubleSelection)
                    {
                        for (int i = 0; i < m_SelectedPoints.Count; i++)
                        {
                            if (m_SelectedPoints[i].m_vPosition == m_vSelectedPosition)
                                m_SelectedPoints.RemoveAt(i);
                        }
                    }
                }
                
                    
            }
        }

        private void Plot3DControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (d3dDevice == null) return;

            if(e.Button== MouseButtons.Right && m_RMBDownPos==e.Location)
            {
                //show context menu, do nothing else
                contextMenuStrip1.Show(this,e.Location);
                return;
            }

            if (m_bSelectionMode)
            {
                if (m_vSelectionStart != m_vSelectionEnd)
                {
                    //valid selection, make it happen!

                    m_PlotZoom.m_fMZOffset = m_PlotZoom.m_fMZOffset + m_PlotZoom.m_fMZSubset * (1 - (Math.Max(m_vSelectionStart.Z, m_vSelectionEnd.Z) + m_vPlotArea.Z / 2) / m_vPlotArea.Z);
                    m_PlotZoom.m_fRTOffset = m_PlotZoom.m_fRTOffset + m_PlotZoom.m_fRTSubset * (Math.Min(m_vSelectionStart.X, m_vSelectionEnd.X) + m_vPlotArea.X / 2) / m_vPlotArea.X;



                    m_PlotZoom.m_fMZSubset = m_PlotZoom.m_fMZSubset * (Math.Abs(m_vSelectionStart.Z - m_vSelectionEnd.Z) / m_vPlotArea.Z);
                    m_PlotZoom.m_fRTSubset = m_PlotZoom.m_fRTSubset * (Math.Abs(m_vSelectionStart.X - m_vSelectionEnd.X) / m_vPlotArea.X);


                    if (m_PlotZoom.m_fMZSubset < m_fMinSubset)
                        m_PlotZoom.m_fMZSubset = m_fMinSubset;

                    if (m_PlotZoom.m_fRTSubset < m_fMinSubset)
                        m_PlotZoom.m_fRTSubset = m_fMinSubset;

                    if (m_PlotZoom.m_fMZOffset < 0)
                        m_PlotZoom.m_fMZOffset = 0;

                    if (m_PlotZoom.m_fRTOffset < 0)
                        m_PlotZoom.m_fRTOffset = 0;


                    if (m_PlotZoom.m_fMZSubset + m_PlotZoom.m_fMZOffset > 1)
                        m_PlotZoom.m_fMZOffset = 1 - m_PlotZoom.m_fMZSubset;


                    if (m_PlotZoom.m_fRTSubset + m_PlotZoom.m_fRTOffset > 1)
                        m_PlotZoom.m_fRTOffset = 1 - m_PlotZoom.m_fRTSubset;


                    WidgetPlot3D parent = Parent.Parent as WidgetPlot3D;
                    m_iCurrentDataZoom = (int)Math.Max((1 / m_fMinSubset - m_PlotZoom.m_fMZSubset / m_fMinSubset) * 5,
                        (1 / m_fMinSubset - m_PlotZoom.m_fRTSubset / m_fMinSubset) * 5);
                    parent.SetZoom(m_iCurrentDataZoom);

                    //m_iCurrentZoomMZ = (int)(1 / m_fMinSubset - m_PlotZoom.m_fMZSubset / m_fMinSubset) * 5;
                    //m_iCurrentZoomRT = (int)(1 / m_fMinSubset - m_PlotZoom.m_fRTSubset / m_fMinSubset) * 5;

                    m_vSelectionStart = new Vector3(0, 0, 0);
                    m_vSelectionEnd = new Vector3(0, 0, 0);
                    Refresh();
                }
            }
        }

        private void zoomInToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetDataZoom(m_iCurrentDataZoom + 10);
            (Parent.Parent as WidgetPlot3D).SetZoom(m_iCurrentDataZoom);
        }

        private void zoomOutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetDataZoom(m_iCurrentDataZoom - 10);
            (Parent.Parent as WidgetPlot3D).SetZoom(m_iCurrentDataZoom);
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            WidgetPlot3D parent = (WidgetPlot3D)Parent.Parent;
            parent.SaveScreenshot();
        }


    }
}
